├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── greetings.yml │ ├── maven-central-push.yml │ ├── maven-pr.yml │ ├── pr-review.yml │ └── update-wiki.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── checkstyle.xml ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── sngular │ │ │ └── annotation │ │ │ ├── pact │ │ │ ├── DslExclude.java │ │ │ ├── Example.java │ │ │ └── PactDslBodyBuilder.java │ │ │ └── processor │ │ │ ├── PactDslProcessor.java │ │ │ ├── exception │ │ │ ├── PactProcessorException.java │ │ │ ├── TemplateFactoryException.java │ │ │ └── TemplateGenerationException.java │ │ │ ├── mapping │ │ │ ├── BigDecimalMapping.java │ │ │ ├── BigIntegerMapping.java │ │ │ ├── BooleanMapping.java │ │ │ ├── ByteMapping.java │ │ │ ├── CharMapping.java │ │ │ ├── CustomDslModifier.java │ │ │ ├── DateMapping.java │ │ │ ├── DoubleMapping.java │ │ │ ├── FloatMapping.java │ │ │ ├── IntegerMapping.java │ │ │ ├── LongMapping.java │ │ │ ├── ShortMapping.java │ │ │ ├── StringMapping.java │ │ │ ├── TypeMapping.java │ │ │ └── ZonedDateTimeMapping.java │ │ │ ├── model │ │ │ ├── ClassBuilderTemplate.java │ │ │ ├── DslComplexField.java │ │ │ ├── DslComplexTypeEnum.java │ │ │ ├── DslField.java │ │ │ ├── DslSimpleField.java │ │ │ └── FieldValidations.java │ │ │ └── template │ │ │ ├── ClasspathTemplateLoader.java │ │ │ └── TemplateFactory.java │ └── resources │ │ └── templates │ │ └── templateDslBuilder.ftlh └── test │ ├── java │ └── com │ │ └── sngular │ │ └── annotation │ │ └── processor │ │ ├── BasicDataTypeTest.java │ │ ├── DateFormatTest.java │ │ └── RandomValueGenerationTest.java │ └── resources │ ├── basic │ ├── CharacterDataTypes.java │ ├── CharacterDataTypesBuilder.java │ ├── NumericDataTypes.java │ └── NumericDataTypesBuilder.java │ ├── complex │ └── NestedObjects.java │ ├── date │ ├── DateDataTypes.java │ └── DateDataTypesBuilder.java │ └── random │ ├── NumericDataTypes.java │ └── NumericDataTypesBuilder.java └── styles └── OS3_Style_idea.xml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Add the necessary files to be able to reproduce the issue. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | continue-on-error: true 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | issue-message: "Thank you for collaborating with the project by giving us feedback! Cheers!" 14 | pr-message: "Thank you for collaborating with the project to help us improve!" 15 | -------------------------------------------------------------------------------- /.github/workflows/maven-central-push.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven (Push) 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | check-pr-labels: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: read 12 | outputs: 13 | labels: ${{ steps.get-pr.outputs.pr_labels }} 14 | steps: 15 | - name: Get Pull Request information 16 | uses: 8BitJonny/gh-get-current-pr@2.2.0 17 | id: get-pr 18 | 19 | check-version: 20 | runs-on: ubuntu-latest 21 | needs: [check-pr-labels] 22 | if: ${{ contains(needs.check-pr-labels.outputs.labels, 'release') }} 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Check if version is updated 26 | uses: avides/actions-project-version-check@v1.3 27 | id: engine_version_check 28 | with: 29 | token: ${{ secrets.GITHUB_TOKEN }} 30 | file-to-check: pom.xml 31 | continue-on-error: true 32 | 33 | build: 34 | runs-on: ubuntu-latest 35 | needs: [check-version, check-pr-labels] 36 | if: contains(needs.check-version.result, 'success') 37 | steps: 38 | - uses: actions/checkout@v2 39 | with: 40 | ref: ${{ github.head_ref }} 41 | 42 | - name: Set up JDK 17 43 | uses: actions/setup-java@v2 44 | with: 45 | java-version: "17" 46 | distribution: "adopt" 47 | server-id: ossrh 48 | server-username: MAVEN_USERNAME 49 | server-password: MAVEN_CENTRAL_TOKEN 50 | gpg-private-key: ${{ secrets.GPG_SIGNING_KEY }} 51 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 52 | - name: Cache Maven packages 53 | uses: actions/cache@v2 54 | with: 55 | path: ~/.m2 56 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 57 | restore-keys: ${{ runner.os }}-m2 58 | 59 | - name: Publish Maven Plugin to Maven Central Packages 60 | # Uses production profile to sign with gpg plugin 61 | run: | 62 | mvn -B install 63 | mvn -B deploy -P production 64 | env: 65 | MAVEN_USERNAME: ${{ secrets.JIRAID }} 66 | MAVEN_CENTRAL_TOKEN: ${{ secrets.JIRAPASS }} 67 | MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 68 | SIGN_KEY: ${{ secrets.JIRAID }} 69 | SIGN_KEY_ID: ${{ secrets.JIRAPASS }} 70 | SIGN_KEY_PASS: ${{ secrets.GPG_PASSPHRASE }} 71 | 72 | - name: Maven version 73 | id: get-version 74 | run: | 75 | echo "version=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)" | tee $GITHUB_OUTPUT 76 | 77 | - name: Create a Release 78 | uses: marvinpinto/action-automatic-releases@latest 79 | with: 80 | repo_token: ${{ secrets.GITHUB_TOKEN }} 81 | automatic_release_tag: ${{ steps.get-version.outputs.version }} 82 | prerelease: false 83 | title: "Maven Release ${{ steps.get-version.outputs.version }}" 84 | -------------------------------------------------------------------------------- /.github/workflows/maven-pr.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven (PR) 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, ready_for_review, labeled] 6 | 7 | concurrency: pr-${{ github.event.pull_request.id }} 8 | 9 | jobs: 10 | check-version: 11 | runs-on: ubuntu-latest 12 | if: ${{ contains(github.event.pull_request.labels.*.name, 'release') || contains(github.event.pull_request.labels.*.name, 'maven') }} 13 | steps: 14 | - uses: actions/checkout@v3 15 | continue-on-error: true 16 | - name: Check if version is updated 17 | uses: avides/actions-project-version-check@v1.3 18 | id: maven_processor_version_check 19 | with: 20 | token: ${{ secrets.GITHUB_TOKEN }} 21 | file-to-check: pom.xml 22 | continue-on-error: true 23 | - name: Warn about version specification 24 | if: ${{steps.maven_processor_version_check.outcome != 'success' }} 25 | uses: thollander/actions-comment-pull-request@v1 26 | with: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | message: Project version has not been updated in pom.xml. Please, update your version using https://semver.org specifications 29 | - name: Fail 30 | if: ${{steps.maven_processor_version_check.outcome != 'success' }} 31 | uses: cutenode/action-always-fail@v1 32 | - name: New software version 33 | if: ${{ steps.maven_processor_version_check.outcome == 'success' }} 34 | run: echo "New Pact Annotation Builder engine version is " ${{ steps.maven_processor_version_check.version }} 35 | 36 | build: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v3 40 | - name: Set up JDK 17 41 | uses: actions/setup-java@v2 42 | with: 43 | java-version: "17" 44 | distribution: "adopt" 45 | - name: Cache Maven packages 46 | uses: actions/cache@v2 47 | with: 48 | path: ~/.m2 49 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 50 | restore-keys: ${{ runner.os }}-m2 51 | - name: Build Maven Plugin 52 | # Uses production profile to sign with gpg plugin 53 | run: | 54 | mvn -B install 55 | 56 | check-labels: 57 | runs-on: ubuntu-latest 58 | if: github.event.pull_request.mergeable == true 59 | steps: 60 | - name: Warn about missing labels 61 | if: ${{ !contains(github.event.pull_request.labels.*.name, 'release') && !contains(github.event.pull_request.labels.*.name, 'documentation') && !contains(github.event.pull_request.labels.*.name, 'maven') && !contains(github.event.pull_request.labels.*.name, 'gradle')}}} 62 | uses: thollander/actions-comment-pull-request@v2 63 | with: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | message: "This pull request hasn't been labeled as `release`, `gradle`, `maven` nor `documentation`. Please ensure this is intentional before merging." 66 | comment_tag: label-warn 67 | -------------------------------------------------------------------------------- /.github/workflows/pr-review.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Approved 2 | 3 | on: 4 | pull_request_review: 5 | types: [submitted] 6 | 7 | concurrency: pr-${{ github.event.pull_request.id }} 8 | 9 | jobs: 10 | check-labels: 11 | runs-on: ubuntu-latest 12 | if: github.event.review.state == 'approved' 13 | steps: 14 | - name: Warn about missing labels 15 | if: ${{ !contains(github.event.pull_request.labels.*.name, 'release') && !contains(github.event.pull_request.labels.*.name, 'documentation')}}} 16 | uses: thollander/actions-comment-pull-request@v2 17 | with: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | message: "This pull request hasn't been labeled as `release` nor `documentation`. Please ensure this is intentional before merging." 20 | comment_tag: label-warn 21 | -------------------------------------------------------------------------------- /.github/workflows/update-wiki.yml: -------------------------------------------------------------------------------- 1 | name: Sync Docs (Merge) 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | check-pr-labels: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | pull-requests: read 12 | outputs: 13 | labels: ${{ steps.get-pr.outputs.pr_labels }} 14 | steps: 15 | - name: Get Pull Request information 16 | uses: 8BitJonny/gh-get-current-pr@2.2.0 17 | id: get-pr 18 | 19 | update-wiki: 20 | runs-on: ubuntu-latest 21 | needs: [check-pr-labels] 22 | if: contains(needs.check-pr-labels.outputs.labels, 'documentation') 23 | steps: 24 | - name: Setup variables 25 | run: | 26 | echo "REPO_DEST=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}').wiki" >> $GITHUB_ENV 27 | shell: bash 28 | - name: Checkout SCS Pact Annotation Builder Plugin Repository 29 | run: git clone https://github.com/sngular/pact-annotation-builder.git pact-annotation-builder 30 | - name: Checkout SCS Pact Annotation Builder Plugin Wiki Repository 31 | run: git clone https://github.com/sngular/pact-annotation-builder.wiki.git wiki 32 | - name: Copy docs and consolidate links 33 | run: | 34 | cp -rf pact-annotation-builder/docs/* wiki/ 35 | cd wiki 36 | grep -lr "(.*\.md)" *|xargs -r sed -i 's/\.md//g' 37 | echo ">" 38 | git diff-index --quiet HEAD & git status -s 39 | echo "<" 40 | git status 41 | echo ">" 42 | git diff-index --quiet HEAD & git status -s 43 | echo "<" 44 | echo "TO_BE_CANCELLED=$(if [[ $(git diff-index --quiet HEAD & git status -s) ]]; then echo "false"; else echo "true"; fi)" >> $GITHUB_ENV 45 | echo "$TO_BE_CANCELLED" 46 | - name: Setup git user 47 | if: ${{ env.TO_BE_CANCELLED == 'false' }} 48 | run: | 49 | git config --global user.name ${{ github.actor }} 50 | - name: Sync Wiki 51 | if: ${{ env.TO_BE_CANCELLED == 'false' }} 52 | run: | 53 | cd wiki 54 | git status 55 | git add . 56 | git commit -m "pact-annotation-builder documentation | GitHub Actions $GITHUB_WORKFLOW $GITHUB_RUN_NUMBER" 57 | git remote add origin-wiki "https://${{secrets.GITBOT_TOKEN}}@github.com/sngular/pact-annotation-builder.wiki.git" 58 | git push origin-wiki main 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # IntelliJ Files 26 | .idea 27 | *.iml 28 | 29 | # Target Folder 30 | target/ 31 | 32 | # Pom bak file 33 | pom.xml.bak 34 | **/.gradle/ 35 | **/build/ 36 | 37 | # Visual Code 38 | .DS_Store 39 | 40 | **/bin/ 41 | .vscode/ 42 | **/.java-version 43 | 44 | *.bak 45 | dependency-reduced-pom* -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thanks for taking the time to contribute! 4 | 5 | This file includes best practices and guidelines for contributing to our 6 | project. We encourage you to follow them and help us create a better product! 7 | 8 | ## Best practices 9 | 10 | - We follow a [code of conduct](#code-of-conduct), review it and honor it in all 11 | your interactions with the project. 12 | - When contributing to this repository, first create an issue to explain the 13 | change you want to make. 14 | - The issues will be preferably written in English, although Spanish is 15 | also acceptable. 16 | - The issue title should briefly summarize the proposal. 17 | - The issue body should include a description of the proposal with all the 18 | details, conditions, dependencies affected, charts, screenshots, etc., 19 | that can help to further understand and evaluate the proposal. 20 | - Create a new branch before making any change in the repository. This branch 21 | should follow this format: #issue_ID_and_summary (for example, 22 | #23_add_new_provider). 23 | - When writing the code, follow our [style sheet](https://github.com/corunet/scs-multiapi-plugin/tree/main/styles/OSS_style_idea.xml). 24 | - Create commit messages that briefly describe the change applied, preferably in 25 | English. 26 | 27 | ## Pull request process 28 | 29 | 1. Ensure any install or build dependencies are solved before doing a build. 30 | 2. Update the README.md file with details of the changes. This includes new 31 | environment variables, exposed ports, useful file locations and container 32 | parameters. 33 | 3. Increase the version number in any examples files and in README.md to the new 34 | version that this pull request would represent. The versioning scheme we 35 | follow is [SemVer](http://semver.org/). 36 | 4. If your Pull Request corresponds to a new release, label it as `release` to 37 | create a new version of both the maven and gradle plugins. To create a new 38 | release for either of them individually you can use the tags `maven` 39 | or `gradle`. If it updates the documentation, label it as `documentation`. 40 | You can add both a release and a documentation label if needed. 41 | 5. You may squash and merge the pull request once you have the sign-off of two 42 | other developers. If you do not have permission to merge, you may request the 43 | second reviewer to merge the pull request for you. 44 | 45 | ## Code of conduct 46 | 47 | ### Our pledge 48 | 49 | In the interest of fostering an open and welcoming environment, we as 50 | contributors and maintainers pledge to make participation in our project and our 51 | community a harassment-free experience for everyone, regardless of age, body 52 | size, disability, ethnicity, gender identity and expression, level of 53 | experience, nationality, personal appearance, race, religion, or sexual identity 54 | and orientation. 55 | 56 | ### Our standards 57 | 58 | Examples of behavior that contribute to creating a positive environment include: 59 | 60 | - Using welcoming and inclusive language 61 | - Being respectful of differing viewpoints and experiences 62 | - Gracefully accepting constructive criticism 63 | - Focusing on what is best for the community 64 | - Showing empathy towards other community members 65 | 66 | Examples of unacceptable behavior by participants include: 67 | 68 | - The use of sexualized language or imagery and unwelcome sexual attention or 69 | advances 70 | - Trolling, insulting/derogatory comments, and personal or political attacks 71 | - Public or private harassment 72 | - Publishing others' private information, such as a physical or electronic 73 | address, without explicit permission 74 | - Other conduct which could reasonably be considered inappropriate in a 75 | professional setting 76 | 77 | ### Our responsibilities 78 | 79 | Project maintainers are responsible for clarifying the standards of acceptable 80 | behavior and are expected to take appropriate and fair corrective action in 81 | response to any instances of unacceptable behavior. 82 | 83 | Project maintainers have the right and responsibility to remove, edit, or reject 84 | comments, commits, code, wiki edits, issues, and other contributions that do not 85 | comply with this Code of Conduct, or to ban temporarily or permanently any 86 | contributor due to other behaviors that they deem inappropriate, threatening, 87 | offensive, or harmful. 88 | 89 | ### Scope 90 | 91 | This Code of conduct applies both within project spaces and in public spaces 92 | when an individual represents the project or its community. Examples of 93 | representing a project or community include using an official project email 94 | address, posting via an official social media account, or acting as an appointed 95 | representative at an online or offline event. Representation of a project may be 96 | further defined and clarified by project maintainers. 97 | 98 | ### Enforcement 99 | 100 | You can report incidents related to abusive, harassing, or otherwise 101 | unacceptable behavior by contacting the project team 102 | at [os3@sngular.com](mailto:os3@sngular.com). All complaints will be reviewed and 103 | investigated and will result in a response that is deemed necessary and 104 | appropriate to the circumstances. The project team is obligated to maintain 105 | confidentiality concerning the reporter of an incident. Further details of 106 | specific enforcement policies may be posted separately. 107 | 108 | Project maintainers who do not follow or enforce the Code of conduct in good 109 | faith may face temporary or permanent repercussions as determined by other 110 | members of the project's leadership. 111 | 112 | ### Attribution 113 | 114 | This Code of conduct has been adapted from the [Contributor Covenant][homepage], 115 | version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 116 | . 117 | 118 | [homepage]: http://contributor-covenant.org 119 | 120 | [version]: http://contributor-covenant.org/version/1/4/ 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pact DSL Builder 2 | 3 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/67f7406f9b79477faae81cc93ed79395)](https://app.codacy.com/gh/sngular/pact-annotation-processor/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)[![Maven Central](https://img.shields.io/maven-central/v/com.sngular/pact-annotation-processor?label=Maven%20Central)](https://central.sonatype.com/artifact/com.sngular/pact-annotation-processor) 4 | 5 | This is a helper tool for Contract Testing with [Pact](https://docs.pact.io/). 6 | An annotation processor designed to generate DslPart objects for the body definitions based on annotations included in your model classes. 7 | 8 | The focus is to simplify JVM contract testing implementation, minimizing the amount of boilerplate code needed. 9 | 10 | Specially useful when defining body validations for interactions with complex models. 11 | 12 | ## Table of Contents 13 | 14 | - [Pact DSL Builder](#pact-dsl-builder) 15 | - [Getting Started](#getting-started) 16 | - [Requirements](#requirements) 17 | - [Compatibility with pact-jvm](#compatibility-with-pact-jvm) 18 | - [Configuration](#configuration) 19 | - [Usage](#usage) 20 | - [Annotations](#annotations) 21 | - [Builder](#builder) 22 | - [Roadmap](#roadmap) 23 | - [Contributing](#contributing) 24 | - [License](#license) 25 | - [Contact](#contact) 26 | 27 | ## Getting Started 28 | 29 | ### Requirements 30 | 31 | - JDK +17 32 | 33 | - Having inside your project a verification library of your choice to have the `@Max` and `@Min` annotations available, such as [Jakarta](https://central.sonatype.com/artifact/jakarta.validation/jakarta.validation-api), [Spring Boot](https://central.sonatype.com/artifact/org.springframework.boot/spring-boot-starter-validation) or similar. 34 | 35 | ### Compatibility with pact-jvm 36 | 37 | | Pact DSL Builder | Pact JVM | 38 | |:------------------------------------------------------------------------------------:|:----------:| 39 | | [1.1.0](https://central.sonatype.com/artifact/com.sngular/pact-annotation-processor/1.1.0) | +4.6.3 | 40 | | [1.1.7](https://central.sonatype.com/artifact/com.sngular/pact-annotation-processor) | +4.6.3 | 41 | | [1.2.0](https://central.sonatype.com/artifact/com.sngular/pact-annotation-processor/1.1.0) | +4.6.3 | 42 | ### Configuration 43 | 44 | The only configuration needed for starting using the library is adding the dependency to your build automation tool: 45 | 46 | Maven 47 | 48 | ```xml 49 | 50 | ... 51 | 52 | com.sngular 53 | pact-annotation-processor 54 | 1.2.0 55 | 56 | ... 57 | 58 | ``` 59 | 60 | Gradle 61 | 62 | ```groovy 63 | implementation('com.sngular:pact-annotation-processor:1.2.0') 64 | ``` 65 | 66 | ## Usage 67 | 68 | To enable the code generation in a model class, you should annotate it as `@PactDslBodyBuilder`. 69 | That is the only requirement, all other annotations are optional and used for customising the generated code. 70 | 71 | ### Annotations 72 | 73 | We have developed 3 annotations to give support to your needs, 74 | 75 | - `@PactDslBodyBuilder` : To indicate which class you need to generate pact to. 76 | - `@Example`: To define constants values to set in your Pact Body. 77 | - `DslExclude`: To Exclude some property to be included in the builder. 78 | 79 | and support 2 standard Java annotations for validation 80 | 81 | - `@Min`: From Jakarta or Javax (or other validation tools) to indicate the 82 | minimum value to be cover for this property. 83 | - `@Max`: From Jakarta or Javax (or other validation tools) to indicate the 84 | maximum value to be cover for this property. 85 | 86 | | Annotation | Required | Level | Description | 87 | |----------------------:|:--------:|:-----:|:--------------------------------------------------------------------------------------------------------------------------------------------| 88 | | `@PactDslBodyBuilder` | true | Class | Main annotation, to be included in classes that want to be processed. | 89 | | `@Example` | false | Field | Used to provide an specific value for a field. If not present in a field, the value created will be random. | 90 | | `@Min` | false | Field | Defines the maximum value for numeric fields, or number of elements if applied to collections. Will be ignored if an `@Example` is present. | 91 | | `@Max` | false | Field | Defines the minimum value for numeric fields, or number of elements if applied to collections. Will be ignored if an `@Example` is present. | 92 | | `@DslExclude` | false | Field | Ignore de generation of example values. | 93 | 94 | > `@Example` values are always provided as String. If a specific format is 95 | required lets say for date 96 | > and datetime properties, then a format field should be provided, 97 | otherwise it will fall back to default 98 | > format. For date and datetime default format are: 99 | > 100 | > - `yyyy-MM-dd['['ZZZ']']` : for dates 101 | > 102 | > - `yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSS]XXX['['VV']']` : for datetimes. Zone should be provided with this format. 103 | 104 | #### Example 105 | 106 | ```java 107 | package com.sngular.model; 108 | 109 | import com.sngular.annotation.pact.Example; 110 | import com.sngular.annotation.pact.PactDslBodyBuilder; 111 | import jakarta.validation.constraints.Max; 112 | import jakarta.validation.constraints.Min; 113 | 114 | import java.time.ZonedDateTime; 115 | 116 | @PactDslBodyBuilder 117 | public class Address { 118 | 119 | @Example("2023-12-03T10:15:30+01:00[Europe/Madrid]") 120 | private ZonedDateTime deliveryTime; 121 | 122 | @Example("2023-12-13") 123 | private Date creationDate; 124 | 125 | @Example("Jose") 126 | private String name; 127 | 128 | @Max(12) 129 | @Min(1) 130 | private int number; 131 | 132 | @Example("4") 133 | private long aLong; 134 | 135 | private ZonedDateTime init; 136 | 137 | private City city; 138 | 139 | } 140 | ``` 141 | 142 | ### Builder 143 | 144 | Once the code is compiled, builder will be generated and available under `generated-sources` in your build directory. 145 | 146 | You will need to add the required import, and make use of the related builder class. 147 | 148 | #### Example 149 | 150 | ```java 151 | import com.sngular.model.AddressBuilder; 152 | 153 | AddressBuilder addressBuilder = new AddressBuilder(); 154 | DslPart bodyDslPart = addressBuilder.build(); 155 | 156 | 157 | @Pact(consumer = "consumer-poc", provider = "provider-poc") 158 | public RequestResponsePact getStudents(PactDslWithProvider builder) { 159 | AddressBuilder addressBuilder = new AddressBuilder(); 160 | DslPart bodyDslPart = studentBuilder.build(); 161 | 162 | return builder.given("Address exist") 163 | .uponReceiving("get all address") 164 | .path("/address/") 165 | .method("GET") 166 | .willRespondWith() 167 | .status(200) 168 | .headers(Map.of("Content-Type", "application/json")) 169 | .body(bodyDslPart) 170 | .toPact(); 171 | } 172 | ``` 173 | 174 | ## Notes 175 | 176 | - Dates: Regarding Timestamp and Date, we should use keep in mind the default 177 | formats will be used to parse those values: 178 | - For Dates, we are using `"yyyy-MM-dd['['ZZZ']']"` as default format 179 | - For Timestamps, we are using 180 | `"yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSS]XXX['['VV']']"` 181 | as default format for datetime (ZonedDateTime) 182 | If you need and specific format the `@Example` support a format property 183 | to handle them. 184 | 185 | ======= 186 | 187 | ### Expected Instance Builder 188 | 189 | In certain situations, especially when using the `@Example` annotation in all 190 | of your model data objects, you may prefer to perform classic manual 191 | instance creation for your validations in the `@Test` methods: 192 | 193 | ```java 194 | @Test 195 | @PactTestFor(pactMethod = "getAddressTest") 196 | void getAddressTest(MockServer mockServer) { 197 | RestTemplate restTemplate = new RestTemplateBuilder().rootUri(mockServer.getUrl()).build(); 198 | Address response = new BasicService(restTemplate).getAdress("..."); 199 | 200 | // Manual instance creation for validation 201 | Address expectedAddress = new Address(); 202 | expectedAddress.setName("Jose"); 203 | expectedAddress.setNumber(12); 204 | // ... 205 | 206 | assertEquals(expectedAddress, response); 207 | } 208 | ``` 209 | 210 | However, in many situations, especially when dealing with random values being generated, 211 | you may prefer to delegate the instance creation to the library itself. 212 | This is the purpose of the `buildExpectedInstance()` method, which generates an 213 | instance of the model object initialized with the values generated (or set 214 | by you with `@Example`) for the given object: 215 | 216 | ```java 217 | 218 | @Test 219 | @PactTestFor(pactMethod = "getAddressTest") 220 | void getAddressTest(MockServer mockServer) { 221 | RestTemplate restTemplate = new RestTemplateBuilder().rootUri(mockServer.getUrl()).build(); 222 | Address response = new BasicService(restTemplate).getAdress("..."); 223 | 224 | // Using buildExpectedInstance() for instance creation 225 | Address expectedAddress = new AddressBuilder().buildExpectedInstance(); 226 | 227 | assertEquals(expectedAddress, response); 228 | } 229 | 230 | ``` 231 | 232 | ## Roadmap 233 | 234 | Roadmap available under [GitHub Projects section](https://github.com/orgs/sngular/projects/2). 235 | 236 | See the [open issues](https://github.com/sngular/pact-annotation-processor/issues) for a full list of proposed features (and known issues). 237 | 238 | ## Contributing 239 | 240 | Contributions are what makes the open source community special. Any contributions you make are greatly appreciated. 241 | 242 | If you have a suggestion that would make this library better, 243 | please [review our contributing guidelines](https://github.com/sngular/pact-annotation-processor/blob/main/CONTRIBUTING.md). 244 | 245 | Or you can simply [open a feature request issue](https://github.com/sngular/pact-annotation-processor/issues/new/choose). 246 | 247 | ## License 248 | 249 | Distributed under Mozilla Public License Version 2.0. See [LICENSE](https://github.com/sngular/pact-annotation-processor/blob/main/LICENSE) for more information 250 | 251 | ## Contact 252 | 253 | OS3 team: [os3@sngular.com](mailto:os3@sngular.com) 254 | 255 | Sngular - [GitHub Org](https://github.com/sngular) 256 | 257 | 258 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.sngular 6 | pact-annotation-processor 7 | 1.2.2 8 | 9 | PactDslBuilder - Annotation Processor 10 | Pact DSL Builder annotation processor. 11 | https://github.com/sngular/pactannotationprocessor 12 | 13 | 14 | Mozilla Public License 2.0 15 | https://github.com/sngular/pactannotationprocessor/blob/main/LICENSE 16 | repo 17 | 18 | 19 | 20 | 21 | 22 | jegarcia 23 | Jose Enrique Garcia Maciñeiras 24 | joseenrique.garcia@sngular.com 25 | https://sngular.github.io/ 26 | 27 | Principal Engineer 28 | Team Leader 29 | 30 | Europe/Madrid 31 | 32 | 33 | ana.villa 34 | Ana Villa Caballero 35 | ana.villa@sngular.com 36 | https://sngular.github.io/ 37 | 38 | Senior Developer 39 | 40 | Europe/Madrid 41 | 42 | 43 | apenlor 44 | Alejandro Pena Lorenzo 45 | alejandro.pena@sngular.com 46 | https://sngular.github.io/ 47 | 48 | Principal Engineer 49 | Team Leader 50 | 51 | America/New_York 52 | 53 | 54 | tfdsimoes 55 | Tiago Filipe Domingues Simoes 56 | tfdsimoes+github@gmail.com 57 | 58 | 59 | Software Engineer 60 | 61 | Europe/Madrid 62 | 63 | 64 | MiguelSngularGithub 65 | Miguel Angel Escobar Perez 66 | miguel.escobar@sngular.com 67 | 68 | 69 | Software Engineer 70 | 71 | America/CDMX 72 | 73 | 74 | 75 | 76 | scm:git:git@github.com:sngular/pactannotationprocessorr.git 77 | scm:git:git@github.com:sngular/pactannotationprocessorr.git 78 | scm:git:git@github.com:sngular/pactannotationprocessorr.git 79 | 80 | 81 | 82 | 83 | ossrh 84 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 85 | 86 | 87 | ossrh 88 | https://s01.oss.sonatype.org/content/repositories/snapshots 89 | 90 | 91 | 92 | 93 | 17 94 | 17 95 | 2.3.32 96 | 1.18.30 97 | 3.13.0 98 | 1.1.1 99 | 4.6.3 100 | 20230618 101 | 2.3.4 102 | 4.1.95.Final 103 | 4.4 104 | 1.15 105 | 2.8.9 106 | 32.0.0-jre 107 | 3.3.0 108 | 3.6.0 109 | 3.0.0 110 | 1.5 111 | 112 | 3.24.2 113 | 5.9.2 114 | 5.3.1 115 | 116 | 117 | 118 | 119 | org.freemarker 120 | freemarker 121 | ${freemarker.version} 122 | 123 | 124 | org.projectlombok 125 | lombok 126 | ${projectlombok.version} 127 | 128 | 129 | org.apache.commons 130 | commons-lang3 131 | ${commons-lang3.version} 132 | 133 | 134 | com.google.auto.service 135 | auto-service 136 | ${google-auto.version} 137 | provided 138 | 139 | 140 | au.com.dius.pact 141 | consumer 142 | ${pact-consumer.version} 143 | 144 | 145 | commons-collections 146 | commons-collections 147 | 148 | 149 | 150 | 151 | org.apache.commons 152 | commons-collections4 153 | 4.4 154 | 155 | 156 | org.apache.commons 157 | commons-rng-simple 158 | ${apache-commons-rng.version} 159 | 160 | 161 | commons-codec 162 | commons-codec 163 | ${commons-codec.version} 164 | 165 | 166 | com.google.code.gson 167 | gson 168 | ${google-gson.version} 169 | 170 | 171 | com.google.guava 172 | guava 173 | ${google-guava.version} 174 | 175 | 176 | 177 | com.google.testing.compile 178 | compile-testing 179 | 0.21.0 180 | test 181 | 182 | 183 | org.assertj 184 | assertj-core 185 | ${assertj-core.version} 186 | test 187 | 188 | 189 | org.assertj 190 | assertj-core 191 | 192 | 193 | 194 | 195 | org.junit.jupiter 196 | junit-jupiter-engine 197 | ${junit-jupiter-engine.version} 198 | test 199 | 200 | 201 | org.junit.jupiter 202 | junit-jupiter-params 203 | ${junit-jupiter-engine.version} 204 | test 205 | 206 | 207 | org.mockito 208 | mockito-core 209 | ${mockito.version} 210 | test 211 | 212 | 213 | org.mockito 214 | mockito-junit-jupiter 215 | ${mockito.version} 216 | test 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | org.apache.maven.plugins 225 | maven-checkstyle-plugin 226 | 3.3.0 227 | 228 | UTF-8 229 | UTF-8 230 | true 231 | true 232 | false 233 | 234 | 235 | 236 | com.puppycrawl.tools 237 | checkstyle 238 | 10.2 239 | 240 | 241 | 242 | 243 | com.github.ekryd.sortpom 244 | sortpom-maven-plugin 245 | ${sortpom-maven-plugin.version} 246 | 247 | 248 | 249 | 250 | 251 | org.apache.maven.plugins 252 | maven-surefire-plugin 253 | 3.1.2 254 | 255 | 256 | --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED 257 | --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED 258 | --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED 259 | 260 | 261 | 262 | 263 | default-test 264 | 265 | test 266 | 267 | 268 | 269 | 270 | 271 | org.apache.maven.plugins 272 | maven-checkstyle-plugin 273 | 274 | checkstyle.xml 275 | 276 | 277 | 278 | com.puppycrawl.tools 279 | checkstyle 280 | 10.2 281 | 282 | 283 | 284 | 285 | validate 286 | 287 | check 288 | 289 | validate 290 | 291 | 292 | 293 | 294 | org.apache.maven.plugins 295 | maven-compiler-plugin 296 | 3.11.0 297 | 298 | 299 | org.apache.maven.plugins 300 | maven-resources-plugin 301 | 3.3.1 302 | 303 | 304 | org.apache.maven.plugins 305 | maven-shade-plugin 306 | 3.5.0 307 | 308 | false 309 | true 310 | true 311 | 312 | 313 | 314 | make-shadow 315 | 316 | shade 317 | 318 | package 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | org.apache.maven.plugins 329 | maven-checkstyle-plugin 330 | 3.3.0 331 | 332 | ../checkstyle.xml 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | production 341 | 342 | 343 | production 344 | true 345 | 346 | 347 | 348 | 349 | 350 | org.apache.maven.plugins 351 | maven-source-plugin 352 | ${maven-source-plugin.version} 353 | 354 | 355 | attach-sources 356 | 357 | jar 358 | 359 | 360 | 361 | 362 | 363 | org.apache.maven.plugins 364 | maven-javadoc-plugin 365 | ${maven-javadoc-plugin.version} 366 | 367 | 368 | attach-javadocs 369 | 370 | jar 371 | 372 | 373 | 374 | 375 | 376 | org.apache.maven.plugins 377 | maven-gpg-plugin 378 | 3.1.0 379 | 380 | 381 | sign-artifacts 382 | 383 | sign 384 | 385 | verify 386 | 387 | 388 | --pinentry-mode 389 | loopback 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/pact/DslExclude.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.pact; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.FIELD) 9 | @Retention(RetentionPolicy.SOURCE) 10 | public @interface DslExclude { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/pact/Example.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.pact; 8 | 9 | import java.lang.annotation.ElementType; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | @Target(ElementType.FIELD) 15 | @Retention(RetentionPolicy.SOURCE) 16 | public @interface Example { 17 | 18 | String value() default ""; 19 | 20 | String format() default ""; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/pact/PactDslBodyBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.pact; 8 | 9 | import java.lang.annotation.ElementType; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | @Target(ElementType.TYPE) 15 | @Retention(RetentionPolicy.SOURCE) 16 | public @interface PactDslBodyBuilder { 17 | 18 | String[] customModifiers() default {}; 19 | 20 | } -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/PactDslProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Objects; 14 | import java.util.Optional; 15 | import java.util.Random; 16 | import java.util.Set; 17 | 18 | import javax.annotation.processing.AbstractProcessor; 19 | import javax.annotation.processing.Processor; 20 | import javax.annotation.processing.RoundEnvironment; 21 | import javax.annotation.processing.SupportedAnnotationTypes; 22 | import javax.lang.model.SourceVersion; 23 | import javax.lang.model.element.AnnotationMirror; 24 | import javax.lang.model.element.AnnotationValue; 25 | import javax.lang.model.element.Element; 26 | import javax.lang.model.element.ExecutableElement; 27 | import javax.lang.model.element.Name; 28 | import javax.lang.model.element.TypeElement; 29 | import javax.lang.model.type.DeclaredType; 30 | import javax.lang.model.util.Elements; 31 | import javax.lang.model.util.Types; 32 | 33 | import com.google.auto.service.AutoService; 34 | import com.google.common.collect.ImmutableMap; 35 | import com.sngular.annotation.pact.DslExclude; 36 | import com.sngular.annotation.pact.Example; 37 | import com.sngular.annotation.pact.PactDslBodyBuilder; 38 | import com.sngular.annotation.processor.exception.PactProcessorException; 39 | import com.sngular.annotation.processor.exception.TemplateFactoryException; 40 | import com.sngular.annotation.processor.exception.TemplateGenerationException; 41 | import com.sngular.annotation.processor.mapping.BigDecimalMapping; 42 | import com.sngular.annotation.processor.mapping.BigIntegerMapping; 43 | import com.sngular.annotation.processor.mapping.BooleanMapping; 44 | import com.sngular.annotation.processor.mapping.ByteMapping; 45 | import com.sngular.annotation.processor.mapping.CharMapping; 46 | import com.sngular.annotation.processor.mapping.DateMapping; 47 | import com.sngular.annotation.processor.mapping.DoubleMapping; 48 | import com.sngular.annotation.processor.mapping.FloatMapping; 49 | import com.sngular.annotation.processor.mapping.IntegerMapping; 50 | import com.sngular.annotation.processor.mapping.LongMapping; 51 | import com.sngular.annotation.processor.mapping.ShortMapping; 52 | import com.sngular.annotation.processor.mapping.StringMapping; 53 | import com.sngular.annotation.processor.mapping.TypeMapping; 54 | import com.sngular.annotation.processor.mapping.ZonedDateTimeMapping; 55 | import com.sngular.annotation.processor.model.ClassBuilderTemplate; 56 | import com.sngular.annotation.processor.model.DslComplexField; 57 | import com.sngular.annotation.processor.model.DslComplexTypeEnum; 58 | import com.sngular.annotation.processor.model.DslField; 59 | import com.sngular.annotation.processor.model.DslSimpleField; 60 | import com.sngular.annotation.processor.model.FieldValidations; 61 | import com.sngular.annotation.processor.template.ClasspathTemplateLoader; 62 | import com.sngular.annotation.processor.template.TemplateFactory; 63 | import freemarker.template.TemplateException; 64 | import lombok.extern.slf4j.Slf4j; 65 | import org.apache.commons.collections4.CollectionUtils; 66 | import org.apache.commons.collections4.IterableUtils; 67 | import org.apache.commons.collections4.IteratorUtils; 68 | import org.apache.commons.lang3.StringUtils; 69 | import org.apache.commons.lang3.math.NumberUtils; 70 | import org.apache.commons.rng.RestorableUniformRandomProvider; 71 | import org.apache.commons.rng.simple.RandomSource; 72 | import org.jetbrains.annotations.NotNull; 73 | 74 | @Slf4j 75 | @AutoService(Processor.class) 76 | @SupportedAnnotationTypes("com.sngular.annotation.pact.PactDslBodyBuilder") 77 | public class PactDslProcessor extends AbstractProcessor { 78 | 79 | static final Map> TYPE_MAPPING = ImmutableMap.>builder() 80 | .put("int", new IntegerMapping()) 81 | .put("Integer", new IntegerMapping()) 82 | .put("BigInteger", new BigIntegerMapping()) 83 | .put("short", new ShortMapping()) 84 | .put("Short", new ShortMapping()) 85 | .put("byte", new ByteMapping()) 86 | .put("Byte", new ByteMapping()) 87 | .put("long", new LongMapping()) 88 | .put("Long", new LongMapping()) 89 | .put("char", new CharMapping()) 90 | .put("Character", new CharMapping()) 91 | .put("String", new StringMapping()) 92 | .put("float", new FloatMapping()) 93 | .put("Float", new FloatMapping()) 94 | .put("double", new DoubleMapping()) 95 | .put("Double", new DoubleMapping()) 96 | .put("BigDecimal", new BigDecimalMapping()) 97 | .put("boolean", new BooleanMapping()) 98 | .put("Boolean", new BooleanMapping()) 99 | .put("date", new DateMapping()) 100 | .put("java.time.ZonedDateTime", new ZonedDateTimeMapping()) 101 | .put("ZonedDateTime", new ZonedDateTimeMapping()) 102 | .put("java.util.Date", new DateMapping()) 103 | .put("Date", new DateMapping()) 104 | .build(); 105 | 106 | private static final String CUSTOM_MODIFIERS = "customModifiers"; 107 | 108 | private Elements elementUtils; 109 | 110 | private Types typeUtils; 111 | 112 | private RestorableUniformRandomProvider randomSource = RandomSource.XO_RO_SHI_RO_128_PP.create(); 113 | 114 | public PactDslProcessor() { 115 | } 116 | 117 | public PactDslProcessor(final RestorableUniformRandomProvider randomSource) { 118 | this.randomSource = randomSource; 119 | } 120 | 121 | @NotNull 122 | private static List getFieldElements(final Element element) { 123 | return IterableUtils.toList(IterableUtils.filteredIterable(element.getEnclosedElements(), elt -> elt.getKind().isField())); 124 | } 125 | 126 | private static String getFormat(final Element fieldElement, final String defaultFormat) { 127 | final String value = fieldElement.getAnnotation(Example.class).format(); 128 | return StringUtils.defaultIfEmpty(value, defaultFormat); 129 | } 130 | 131 | @Override 132 | public final SourceVersion getSupportedSourceVersion() { 133 | return SourceVersion.latestSupported(); 134 | } 135 | 136 | @Override 137 | public final boolean process(final Set annotations, final RoundEnvironment roundEnv) { 138 | 139 | final TemplateFactory templateFactory; 140 | try { 141 | templateFactory = new TemplateFactory(); 142 | } catch (final TemplateException e) { 143 | throw new TemplateFactoryException(e); 144 | } 145 | elementUtils = processingEnv.getElementUtils(); 146 | typeUtils = processingEnv.getTypeUtils(); 147 | final Set elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(PactDslBodyBuilder.class); 148 | IteratorUtils 149 | .transformedIterator(elementsAnnotatedWith.iterator(), this::composeBuilderTemplate).forEachRemaining(builderTemplate -> { 150 | try { 151 | final var builderFile = processingEnv.getFiler().createSourceFile(builderTemplate.completePath()); 152 | templateFactory.writeTemplateToFile(ClasspathTemplateLoader.TEMPLATE_DSL_BUILDER, builderTemplate, builderFile.openWriter()); 153 | } catch (IOException | TemplateException e) { 154 | throw new TemplateGenerationException("PactDslBodyBuilder", e); 155 | } 156 | }); 157 | return true; 158 | } 159 | 160 | private ClassBuilderTemplate composeBuilderTemplate(final Element element) { 161 | final List fieldElements = getFieldElements(element); 162 | final var qualifiedName = ((TypeElement) element).getQualifiedName().toString(); 163 | String packageName = null; 164 | final int lastDot = qualifiedName.lastIndexOf('.'); 165 | if (lastDot > 0) { 166 | packageName = qualifiedName.substring(0, lastDot); 167 | } 168 | 169 | final var builderSimpleClassName = qualifiedName.substring(lastDot + 1); 170 | final var builderClassName = builderSimpleClassName + "Builder"; 171 | return ClassBuilderTemplate.builder() 172 | .fileName(builderClassName) 173 | .className(builderSimpleClassName) 174 | .modelPackage(packageName) 175 | .fieldList(getFields(fieldElements)) 176 | .customModifiers(extractCustomModifiers(element)) 177 | .build(); 178 | } 179 | 180 | @NotNull 181 | private List getFields(final List fieldElements) { 182 | return IterableUtils.toList(IterableUtils.transformedIterable(fieldElements, fieldElement -> composeDslField(fieldElement, false))); 183 | } 184 | 185 | private List getAnnotationValueAsType(final AnnotationMirror annotationMirror, final String key) { 186 | final var valueAsTypeList = new ArrayList(); 187 | final var annotationValue = getAnnotationValue(annotationMirror, key); 188 | if (annotationValue != null) { 189 | valueAsTypeList.addAll(List.of(annotationValue.toString() 190 | .replace(" ", "").replace("{", "") 191 | .replace("}", "").replace("\"", "") 192 | .split(","))); 193 | } 194 | return valueAsTypeList; 195 | } 196 | 197 | private AnnotationValue getAnnotationValue(final AnnotationMirror annotationMirror, final String key) { 198 | AnnotationValue annotationValue = null; 199 | for (Map.Entry entry : annotationMirror.getElementValues().entrySet()) { 200 | if (entry.getKey().getSimpleName().toString().equals(key)) { 201 | annotationValue = entry.getValue(); 202 | } 203 | } 204 | return annotationValue; 205 | } 206 | 207 | private DslField composeDslField(final Element fieldElement, final boolean insideCollection) { 208 | final DslField result; 209 | final Optional> mappingOp = extractMappingByType(fieldElement); 210 | if (mappingOp.isEmpty()) { 211 | if (checkIfOwn(fieldElement)) { 212 | result = composeDslComplexField(fieldElement); 213 | } else { 214 | final String type = extractType(fieldElement); 215 | if (type.endsWith("List") || type.endsWith("Map") || type.endsWith("Set") || type.endsWith("Collection")) { 216 | result = composeCollection(fieldElement); 217 | } else { 218 | result = composeDslComplexField(fieldElement); 219 | } 220 | } 221 | } else { 222 | result = composeDslSimpleField(fieldElement, mappingOp.get(), insideCollection); 223 | } 224 | return result; 225 | } 226 | 227 | private DslComplexField composeDslComplexField(final Element element) { 228 | return DslComplexField.builder() 229 | .name(element.getSimpleName().toString()) 230 | .fieldType(element.asType().toString()) 231 | .needBuilder(checkIfOwn(element)) 232 | .complexType(DslComplexTypeEnum.OBJECT) 233 | .fieldValidations(extractValidations(element)) 234 | .empty(Objects.nonNull(element.getAnnotation(DslExclude.class))) 235 | .build(); 236 | } 237 | 238 | private DslComplexField composeCollection(final Element element) { 239 | final var typeStr = cleanType(element); 240 | return DslComplexField.builder() 241 | .name(element.getSimpleName().toString()) 242 | .fieldType(typeStr) 243 | .fields(extractTypes(element)) 244 | .fieldValidations(extractValidations(element)) 245 | .complexType(DslComplexTypeEnum.COLLECTION) 246 | .empty(Objects.nonNull(element.getAnnotation(DslExclude.class))) 247 | .build(); 248 | } 249 | 250 | private boolean checkIfOwn(final Element element) { 251 | final var typePackage = elementUtils.getPackageOf(typeUtils.asElement(element.asType())).toString(); 252 | final var parentType = elementUtils.getPackageOf(typeUtils.asElement(element.getEnclosingElement().asType())).toString(); 253 | return parentType.equalsIgnoreCase(typePackage); 254 | } 255 | 256 | private String extractType(final Element element) { 257 | return ((TypeElement) typeUtils.asElement(element.asType())).getQualifiedName().toString(); 258 | } 259 | 260 | private String cleanType(final Element element) { 261 | var finalType = element.asType().toString(); 262 | for (var annotation : element.asType().getAnnotationMirrors()) { 263 | finalType = finalType.replace(annotation.toString(), ""); 264 | } 265 | return finalType.replace(", ", ""); 266 | } 267 | 268 | private FieldValidations extractValidations(final Element element) { 269 | final var validationBuilder = FieldValidations.builder(); 270 | 271 | int minValue = 0; 272 | int maxValue = 0; 273 | final var type = element.asType(); 274 | if (CollectionUtils.isNotEmpty(type.getAnnotationMirrors())) { 275 | for (var annotation : type.getAnnotationMirrors()) { 276 | if (annotation.getAnnotationType().toString().toUpperCase().endsWith("MAX")) { 277 | maxValue = ((Long) Objects.requireNonNull(getAnnotationValue(annotation, "value")).getValue()).intValue(); 278 | validationBuilder.max(maxValue); 279 | } else { 280 | minValue = ((Long) Objects.requireNonNull(getAnnotationValue(annotation, "value")).getValue()).intValue(); 281 | validationBuilder.min(minValue); 282 | } 283 | } 284 | //For random size calculation: defaults to +10 elements max if not defined. 285 | maxValue = (maxValue == 0) ? (minValue + 10) : maxValue; 286 | validationBuilder.randomSize(new Random().nextInt(maxValue - minValue + 1) + minValue); 287 | } 288 | return validationBuilder.build(); 289 | } 290 | 291 | @NotNull 292 | private List extractTypes(final Element element) { 293 | final List listOfFields; 294 | final var listOfCustomMods = new ArrayList<>(CollectionUtils.collect(((DeclaredType) element.asType()).getTypeArguments(), typeUtils::asElement)); 295 | if (listOfCustomMods.size() > 1) { 296 | listOfFields = new ArrayList<>(CollectionUtils.collect(listOfCustomMods, e -> composeDslField(e, true))); 297 | } else { 298 | listOfFields = List.of( 299 | composeDslSimpleField(listOfCustomMods.get(0), 300 | extractMappingByType(listOfCustomMods.get(0)) 301 | .orElseThrow(() -> new PactProcessorException(listOfCustomMods.get(0).getSimpleName().toString())), 302 | true)); 303 | } 304 | return listOfFields; 305 | } 306 | 307 | private List extractCustomModifiers(final Element element) { 308 | final List customModList = new ArrayList<>(); 309 | final var listOfCustomMods = CollectionUtils.collect(element.getAnnotationMirrors(), a -> getAnnotationValueAsType(a, CUSTOM_MODIFIERS)); 310 | CollectionUtils.collect(listOfCustomMods, customModList::addAll); 311 | return customModList; 312 | } 313 | 314 | private DslSimpleField composeDslSimpleField(final Element fieldElement, final TypeMapping mapping, final boolean insideCollection) { 315 | final var validationBuilder = createValidationBuilder(fieldElement); 316 | final var simpleFieldBuilder = createSimpleFieldBuilder(fieldElement, mapping, insideCollection, validationBuilder); 317 | 318 | return simpleFieldBuilder.build(); 319 | } 320 | 321 | private FieldValidations.FieldValidationsBuilder createValidationBuilder(final Element fieldElement) { 322 | final var validationBuilder = FieldValidations.builder(); 323 | 324 | for (var annotation : fieldElement.asType().getAnnotationMirrors()) { 325 | if (annotation.getAnnotationType().toString().toUpperCase().endsWith("MAX")) { 326 | validationBuilder.max(((Long) Objects.requireNonNull(getAnnotationValue(annotation, "value")).getValue()).intValue()); 327 | } else { 328 | validationBuilder.min(((Long) Objects.requireNonNull(getAnnotationValue(annotation, "value")).getValue()).intValue()); 329 | } 330 | } 331 | 332 | return validationBuilder; 333 | } 334 | 335 | private DslSimpleField.DslSimpleFieldBuilder createSimpleFieldBuilder(final Element fieldElement, final TypeMapping mapping, 336 | final boolean insideCollection, final FieldValidations.FieldValidationsBuilder validationBuilder) { 337 | final var simpleFieldBuilder = DslSimpleField.builder() 338 | .name(getNameOrNull(fieldElement.getSimpleName())) 339 | .fieldType(mapping.getFieldType()) 340 | .functionByType(insideCollection ? mapping.getFunctionOnlyValue() : mapping.getFunctionType()) 341 | .onlyValueFunction(insideCollection) 342 | .suffixValue(mapping.getSuffixValue()) 343 | .formatValue(mapping.getFormatValue()) 344 | .fieldValidations(validationBuilder.build()) 345 | .empty(false); 346 | 347 | if (Objects.nonNull(fieldElement.getAnnotation(DslExclude.class))) { 348 | simpleFieldBuilder.empty(true); 349 | } else if (Objects.nonNull(fieldElement.getAnnotation(Example.class))) { 350 | simpleFieldBuilder.defaultValue(getDefaultValue(fieldElement, mapping.getFieldType())); 351 | simpleFieldBuilder.formatValue(getFormat(fieldElement, mapping.getFormatValue())); 352 | } else { 353 | simpleFieldBuilder.defaultValue(mapping.getRandomDefaultValue(validationBuilder.build(), randomSource)); 354 | simpleFieldBuilder.formatValue(mapping.getFormatValue()); 355 | } 356 | 357 | return simpleFieldBuilder; 358 | } 359 | 360 | private String getNameOrNull(final Name simpleName) { 361 | return simpleName.toString().matches("[A-Z].*") ? null : simpleName.toString(); 362 | } 363 | 364 | private static Object getDefaultValue(final Element fieldElement, final String type) { 365 | final Object realValue; 366 | final String value = fieldElement.getAnnotation(Example.class).value(); 367 | if (NumberUtils.isCreatable(value)) { 368 | realValue = switch (type) { 369 | case "int", "Integer" -> NumberUtils.toInt(value); 370 | case "BigInteger" -> NumberUtils.createBigInteger(value); 371 | case "long", "Long" -> NumberUtils.toLong(value); 372 | case "short", "Short" -> NumberUtils.toShort(value); 373 | case "byte", "Byte" -> NumberUtils.toByte(value); 374 | case "float", "Float" -> NumberUtils.toFloat(value); 375 | case "double", "Double" -> NumberUtils.toDouble(value); 376 | case "BigDecimal" -> NumberUtils.createBigDecimal(value); 377 | default -> throw new IllegalStateException("Unexpected value: " + type); 378 | }; 379 | } else { 380 | realValue = value; 381 | } 382 | return realValue; 383 | } 384 | 385 | private Optional> extractMappingByType(final Element element) { 386 | 387 | final var type = element.asType(); 388 | return switch (type.getKind()) { 389 | case BOOLEAN -> Optional.of(TYPE_MAPPING.get("boolean")); 390 | case BYTE -> Optional.of(TYPE_MAPPING.get("byte")); 391 | case SHORT -> Optional.of(TYPE_MAPPING.get("short")); 392 | case INT -> Optional.of(TYPE_MAPPING.get("int")); 393 | case LONG -> Optional.of(TYPE_MAPPING.get("long")); 394 | case CHAR -> Optional.of(TYPE_MAPPING.get("char")); 395 | case FLOAT -> Optional.of(TYPE_MAPPING.get("float")); 396 | case DOUBLE -> Optional.of(TYPE_MAPPING.get("double")); 397 | case DECLARED -> Optional.ofNullable(TYPE_MAPPING.get(this.typeUtils.asElement(type).getSimpleName().toString())); 398 | default -> Optional.empty(); 399 | }; 400 | } 401 | 402 | } 403 | 404 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/exception/PactProcessorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.exception; 8 | 9 | public class PactProcessorException extends RuntimeException { 10 | 11 | private static final String ERROR_MESSAGE = "Error processing element %s"; 12 | 13 | public PactProcessorException(final String ex) { 14 | super(String.format(ERROR_MESSAGE, ex)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/exception/TemplateFactoryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.exception; 8 | 9 | public class TemplateFactoryException extends RuntimeException { 10 | 11 | private static final String ERROR_MESSAGE = "Error processing template factory"; 12 | 13 | public TemplateFactoryException(final Exception ex) { 14 | super(ERROR_MESSAGE, ex); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/exception/TemplateGenerationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.exception; 8 | 9 | public class TemplateGenerationException extends RuntimeException { 10 | 11 | private static final String ERROR_MESSAGE = "Error processing template builder for annotation %s"; 12 | 13 | public TemplateGenerationException(final String annotationName, final Exception ex) { 14 | super(String.format(ERROR_MESSAGE, annotationName), ex); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/BigDecimalMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class BigDecimalMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "BigDecimal"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "decimalType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "decimalValue"; 30 | } 31 | 32 | @Override 33 | public final Number getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final Number randomDefaultValue; 35 | 36 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 37 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), 0); 38 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), (int) Double.MAX_VALUE); 39 | 40 | randomDefaultValue = uniformRandomProvider.nextDouble(minValue, maxValue); 41 | } else { 42 | randomDefaultValue = uniformRandomProvider.nextDouble(Double.MIN_VALUE, Double.MAX_VALUE); 43 | } 44 | 45 | return randomDefaultValue; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/BigIntegerMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class BigIntegerMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "BigInteger"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "integerType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "integerValue"; 30 | } 31 | 32 | @Override 33 | public final Integer getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final int randomDefaultValue; 35 | 36 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 37 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), Integer.MIN_VALUE); 38 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), Integer.MAX_VALUE); 39 | 40 | randomDefaultValue = uniformRandomProvider.nextInt(minValue, maxValue); 41 | } else { 42 | randomDefaultValue = uniformRandomProvider.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); 43 | } 44 | 45 | return randomDefaultValue; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/BooleanMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import com.sngular.annotation.processor.model.FieldValidations; 10 | import org.apache.commons.rng.UniformRandomProvider; 11 | import org.apache.commons.rng.simple.RandomSource; 12 | 13 | public class BooleanMapping implements TypeMapping { 14 | 15 | private final UniformRandomProvider uniformRandomProvider = RandomSource.XO_RO_SHI_RO_128_PP.create(); 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "boolean"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "booleanType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "booleanValue"; 30 | } 31 | 32 | @Override 33 | public final Boolean getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | return uniformRandomProvider.nextBoolean(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/ByteMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class ByteMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "byte"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "integerType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "integerType"; 30 | } 31 | 32 | @Override 33 | public final Integer getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final int result; 35 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 36 | 37 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), (int) Byte.MIN_VALUE); 38 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), (int) Byte.MAX_VALUE); 39 | 40 | result = uniformRandomProvider.nextInt(minValue, maxValue); 41 | } else { 42 | result = uniformRandomProvider.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE); 43 | } 44 | 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/CharMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import com.sngular.annotation.processor.model.FieldValidations; 10 | import org.apache.commons.lang3.RandomStringUtils; 11 | import org.apache.commons.rng.UniformRandomProvider; 12 | 13 | public class CharMapping implements TypeMapping { 14 | 15 | @Override 16 | public final String getFieldType() { 17 | return "char"; 18 | } 19 | 20 | @Override 21 | public final String getFunctionType() { 22 | return "stringType"; 23 | } 24 | 25 | @Override 26 | public final String getFunctionOnlyValue() { 27 | return "stringValue"; 28 | } 29 | 30 | @Override 31 | public final String getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 32 | return RandomStringUtils.randomAlphanumeric(1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/CustomDslModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 10 | 11 | @FunctionalInterface 12 | public interface CustomDslModifier { 13 | 14 | PactDslJsonBody apply(final PactDslJsonBody pactDslJsonBody); 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/DateMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.time.Instant; 10 | import java.util.Date; 11 | 12 | import com.sngular.annotation.processor.model.FieldValidations; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class DateMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "Date"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "date"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "dateValue"; 30 | } 31 | 32 | @Override 33 | public final Date getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | return Date.from(Instant.now()); 35 | } 36 | 37 | @Override 38 | public final String getFormatValue() { 39 | return "yyyy-MM-dd"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/DoubleMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class DoubleMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "double"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "decimalType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "decimalValue"; 30 | } 31 | 32 | @Override 33 | public final Number getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final double result; 35 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 36 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), 0); 37 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), (int) Double.MAX_VALUE); 38 | 39 | result = uniformRandomProvider.nextDouble(minValue, maxValue); 40 | } else { 41 | result = uniformRandomProvider.nextDouble(0, Double.MAX_VALUE); 42 | } 43 | 44 | return result; 45 | } 46 | 47 | @Override 48 | public final String getSuffixValue() { 49 | return "D"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/FloatMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class FloatMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "float"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "decimalType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "decimalValue"; 30 | } 31 | 32 | @Override 33 | public final Number getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final Number randomDefaultValue; 35 | 36 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 37 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), 0); 38 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), (int) Float.MAX_VALUE); 39 | 40 | randomDefaultValue = uniformRandomProvider.nextDouble(minValue, maxValue); 41 | } else { 42 | randomDefaultValue = uniformRandomProvider.nextDouble(0, Float.MAX_VALUE); 43 | } 44 | 45 | return randomDefaultValue; 46 | } 47 | 48 | @Override 49 | public final String getSuffixValue() { 50 | return "F"; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/IntegerMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class IntegerMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "int"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "integerType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "integerValue"; 30 | } 31 | 32 | @Override 33 | public final Integer getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final int result; 35 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 36 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), Integer.MIN_VALUE); 37 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), Integer.MAX_VALUE); 38 | 39 | result = uniformRandomProvider.nextInt(minValue, maxValue); 40 | } else { 41 | result = uniformRandomProvider.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); 42 | } 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/LongMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class LongMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "long"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "integerType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "integerValue"; 30 | } 31 | 32 | @Override 33 | public final Long getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final long result; 35 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 36 | final long minValue = Objects.nonNull(fieldValidations.getMin()) ? fieldValidations.getMin() : Long.MIN_VALUE; 37 | final long maxValue = Objects.nonNull(fieldValidations.getMax()) ? fieldValidations.getMax() : Long.MIN_VALUE; 38 | 39 | result = uniformRandomProvider.nextLong(minValue, maxValue); 40 | } else { 41 | result = uniformRandomProvider.nextLong(Long.MIN_VALUE, Long.MAX_VALUE); 42 | } 43 | 44 | return result; 45 | } 46 | 47 | @Override 48 | public final String getSuffixValue() { 49 | return "L"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/ShortMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.rng.UniformRandomProvider; 14 | 15 | public class ShortMapping implements TypeMapping { 16 | 17 | @Override 18 | public final String getFieldType() { 19 | return "short"; 20 | } 21 | 22 | @Override 23 | public final String getFunctionType() { 24 | return "integerType"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionOnlyValue() { 29 | return "integerValue"; 30 | } 31 | 32 | @Override 33 | public final Integer getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 34 | final int result; 35 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 36 | final int minValue = ObjectUtils.defaultIfNull(fieldValidations.getMin(), (int) Short.MIN_VALUE); 37 | final int maxValue = ObjectUtils.defaultIfNull(fieldValidations.getMax(), (int) Short.MAX_VALUE); 38 | 39 | result = uniformRandomProvider.nextInt(minValue, maxValue); 40 | } else { 41 | 42 | result = uniformRandomProvider.nextInt(Short.MIN_VALUE, Short.MAX_VALUE); 43 | } 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/StringMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.util.Objects; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.lang3.ObjectUtils; 13 | import org.apache.commons.lang3.RandomStringUtils; 14 | import org.apache.commons.rng.UniformRandomProvider; 15 | 16 | public class StringMapping implements TypeMapping { 17 | 18 | public static final int DEFAULT_MAX = 15; 19 | 20 | public static final int DEFAULT_MIN = 1; 21 | 22 | @Override 23 | public final String getFieldType() { 24 | return "String"; 25 | } 26 | 27 | @Override 28 | public final String getFunctionType() { 29 | return "stringType"; 30 | } 31 | 32 | @Override 33 | public final String getFunctionOnlyValue() { 34 | return "stringValue"; 35 | } 36 | 37 | @Override 38 | public final String getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 39 | final int length; 40 | 41 | if (Objects.nonNull(fieldValidations) && ObjectUtils.anyNotNull(fieldValidations.getMin(), fieldValidations.getMax())) { 42 | final int minLength = ObjectUtils.defaultIfNull(fieldValidations.getMin(), DEFAULT_MIN); 43 | final int maxLength = ObjectUtils.defaultIfNull(fieldValidations.getMax(), DEFAULT_MAX); 44 | 45 | length = uniformRandomProvider.nextInt(minLength, maxLength); 46 | } else { 47 | length = uniformRandomProvider.nextInt(DEFAULT_MIN, DEFAULT_MAX); 48 | } 49 | 50 | return RandomStringUtils.randomAlphanumeric(length); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/TypeMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import com.sngular.annotation.processor.model.FieldValidations; 10 | import org.apache.commons.rng.UniformRandomProvider; 11 | 12 | public interface TypeMapping { 13 | 14 | String getFieldType(); 15 | 16 | String getFunctionType(); 17 | 18 | String getFunctionOnlyValue(); 19 | 20 | T getRandomDefaultValue(FieldValidations fieldValidations, UniformRandomProvider uniformRandomProvider); 21 | 22 | default String getSuffixValue() { 23 | return null; 24 | } 25 | 26 | default String getFormatValue() { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/mapping/ZonedDateTimeMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.mapping; 8 | 9 | import java.time.ZonedDateTime; 10 | 11 | import com.sngular.annotation.processor.model.FieldValidations; 12 | import org.apache.commons.rng.UniformRandomProvider; 13 | 14 | public class ZonedDateTimeMapping implements TypeMapping { 15 | 16 | @Override 17 | public final String getFieldType() { 18 | return "ZonedDateTime"; 19 | } 20 | 21 | @Override 22 | public final String getFunctionType() { 23 | return "datetime"; 24 | } 25 | 26 | @Override 27 | public final String getFunctionOnlyValue() { 28 | return "datetimeValue"; 29 | } 30 | 31 | @Override 32 | public final ZonedDateTime getRandomDefaultValue(final FieldValidations fieldValidations, final UniformRandomProvider uniformRandomProvider) { 33 | return ZonedDateTime.now(); 34 | } 35 | 36 | @Override 37 | public final String getFormatValue() { 38 | return "yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSS]XXX['['VV']']"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/model/ClassBuilderTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.model; 8 | 9 | import java.util.List; 10 | 11 | import lombok.Builder; 12 | import lombok.Value; 13 | 14 | @Value 15 | @Builder 16 | public class ClassBuilderTemplate { 17 | 18 | String modelPackage; 19 | 20 | String fileName; 21 | 22 | String className; 23 | 24 | List fieldList; 25 | 26 | List customModifiers; 27 | 28 | public String completePath() { 29 | return String.format("%s.%s", modelPackage, fileName); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/model/DslComplexField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.model; 8 | 9 | import java.util.List; 10 | 11 | import lombok.Builder; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.NonNull; 14 | import lombok.Value; 15 | 16 | @Value 17 | @EqualsAndHashCode(callSuper = true) 18 | public class DslComplexField extends DslField { 19 | 20 | @NonNull DslComplexTypeEnum complexType; 21 | 22 | List fields; 23 | 24 | String fieldType; 25 | 26 | FieldValidations fieldValidations; 27 | 28 | boolean needBuilder; 29 | 30 | boolean empty; 31 | 32 | @Builder 33 | private DslComplexField( 34 | final String name, @NonNull final DslComplexTypeEnum complexType, final List fields, final String fieldType, final FieldValidations fieldValidations, 35 | final boolean needBuilder, final boolean empty) { 36 | super(name); 37 | this.complexType = complexType; 38 | this.fields = fields; 39 | this.fieldType = fieldType; 40 | this.fieldValidations = fieldValidations; 41 | this.needBuilder = needBuilder; 42 | this.empty = empty; 43 | } 44 | 45 | public static class DslComplexFieldBuilder {} 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/model/DslComplexTypeEnum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.model; 8 | 9 | public enum DslComplexTypeEnum { 10 | COLLECTION, 11 | OBJECT 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/model/DslField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.model; 8 | 9 | import lombok.AllArgsConstructor; 10 | import lombok.Getter; 11 | 12 | @Getter 13 | @AllArgsConstructor 14 | public class DslField { 15 | 16 | private String name; 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/model/DslSimpleField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.model; 8 | 9 | import lombok.Builder; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.Value; 12 | 13 | @Value 14 | @EqualsAndHashCode(callSuper = true) 15 | public class DslSimpleField extends DslField { 16 | 17 | String fieldType; 18 | 19 | String functionByType; 20 | 21 | Object defaultValue; 22 | 23 | String suffixValue; 24 | 25 | String formatValue; 26 | 27 | FieldValidations fieldValidations; 28 | 29 | boolean onlyValueFunction; 30 | 31 | boolean empty; 32 | 33 | @Builder 34 | private DslSimpleField( 35 | final String name, final String fieldType, final String functionByType, final Object defaultValue, final String suffixValue, final String formatValue, 36 | final FieldValidations fieldValidations, 37 | final boolean onlyValueFunction, final boolean empty) { 38 | super(name); 39 | this.fieldType = fieldType; 40 | this.functionByType = functionByType; 41 | this.defaultValue = defaultValue; 42 | this.suffixValue = suffixValue; 43 | this.formatValue = formatValue; 44 | this.fieldValidations = fieldValidations; 45 | this.onlyValueFunction = onlyValueFunction; 46 | this.empty = empty; 47 | } 48 | 49 | public static class DslSimpleFieldBuilder {} 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/model/FieldValidations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.model; 8 | 9 | import lombok.Builder; 10 | import lombok.Value; 11 | 12 | @Value 13 | @Builder 14 | public class FieldValidations { 15 | 16 | Integer min; 17 | 18 | Integer max; 19 | 20 | Integer randomSize; 21 | 22 | String regex; 23 | 24 | public static class FieldValidationsBuilder {} 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/template/ClasspathTemplateLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.template; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.Reader; 12 | import java.io.StringReader; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Objects; 17 | 18 | import freemarker.cache.TemplateLoader; 19 | 20 | public class ClasspathTemplateLoader implements TemplateLoader { 21 | 22 | public static final String TEMPLATE_DSL_BUILDER = "templateDslBuilder.ftlh"; 23 | 24 | private static final List TEMPLATE_FILES = List.of(TEMPLATE_DSL_BUILDER); 25 | 26 | private static final ClassLoader LOADER = ClasspathTemplateLoader.class.getClassLoader(); 27 | 28 | private static final String TEMPLATES_PATH = "templates/"; 29 | 30 | private final Map templatesMap = new HashMap<>(); 31 | 32 | public ClasspathTemplateLoader() { 33 | templatesMap.putAll(getResourceFolderFiles()); 34 | } 35 | 36 | private Map getResourceFolderFiles() { 37 | final Map templates = new HashMap<>(); 38 | try { 39 | for (var templateFile : TEMPLATE_FILES) { 40 | templates.put(templateFile, 41 | readFile((InputStream) Objects.requireNonNull(LOADER.getResource(TEMPLATES_PATH + templateFile)).getContent())); 42 | } 43 | } catch (final IOException e) { 44 | e.printStackTrace(); 45 | } 46 | return templates; 47 | 48 | } 49 | 50 | private String readFile(final InputStream file) throws IOException { 51 | return new String(file.readAllBytes()); 52 | } 53 | 54 | @Override 55 | public final Object findTemplateSource(final String templateName) { 56 | return templatesMap.get(templateName); 57 | } 58 | 59 | @Override 60 | public final long getLastModified(final Object o) { 61 | return 0; 62 | } 63 | 64 | @Override 65 | public final Reader getReader(final Object template, final String charSet) { 66 | return new StringReader(template.toString()); 67 | } 68 | 69 | @Override 70 | public void closeTemplateSource(final Object o) { 71 | // Not required to implement 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/sngular/annotation/processor/template/TemplateFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package com.sngular.annotation.processor.template; 8 | 9 | import java.io.IOException; 10 | import java.io.Writer; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | import com.sngular.annotation.processor.model.ClassBuilderTemplate; 15 | import freemarker.template.Configuration; 16 | import freemarker.template.Template; 17 | import freemarker.template.TemplateException; 18 | import freemarker.template.TemplateExceptionHandler; 19 | 20 | public class TemplateFactory { 21 | 22 | private static final String SCHEMA_ROOT = "schema"; 23 | 24 | private final Configuration cfg = new Configuration(Configuration.VERSION_2_3_32); 25 | 26 | public TemplateFactory() throws TemplateException { 27 | cfg.setTemplateLoader(new ClasspathTemplateLoader()); 28 | cfg.setDefaultEncoding("UTF-8"); 29 | cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); 30 | cfg.setLogTemplateExceptions(true); 31 | cfg.setSetting("c_format", "Java"); 32 | } 33 | 34 | public final void writeTemplateToFile(final String templateName, final ClassBuilderTemplate classBuilder, final Writer writer) throws IOException, TemplateException { 35 | final Template template = cfg.getTemplate(templateName); 36 | 37 | final Map root = new HashMap<>(); 38 | root.put(SCHEMA_ROOT, classBuilder); 39 | template.process(root, writer); 40 | writer.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/templates/templateDslBuilder.ftlh: -------------------------------------------------------------------------------- 1 | <#assign constructor = false> 2 | <#macro writeAttrField field> 3 | <#if field.functionByType??> 4 | <#if field.fieldType == "boolean"> 5 | ${field.fieldType} ${field.name} = ${field.defaultValue?string}; 6 | <#elseif field.fieldType == "char"> 7 | String ${field.name} = "${field.defaultValue}"; 8 | <#elseif field.defaultValue?is_number> 9 | <#if field.fieldType == "BigInteger"> 10 | BigInteger ${field.name} = new BigInteger("${field.defaultValue?c}"); 11 | <#elseif field.fieldType == "BigDecimal"> 12 | BigDecimal ${field.name} = new BigDecimal("${field.defaultValue?c}"); 13 | <#else> 14 | ${field.fieldType} ${field.name} = ${field.defaultValue?c}<#if field.suffixValue?has_content>${field.suffixValue}; 15 | 16 | <#elseif field.defaultValue?is_boolean> 17 | ${field.fieldType} ${field.name} = ${field.defaultValue?then('true', 'false')}; 18 | <#elseif field.functionByType == "date"> 19 | <#assign constructor = true> 20 | ${field.fieldType} ${field.name} = new SimpleDateFormat("${field.formatValue?no_esc}").parse("${field.defaultValue?j_string?no_esc}"); 21 | <#elseif field.functionByType == "datetime" && field.formatValue?has_content> 22 | ${field.fieldType} ${field.name} = ZonedDateTime.parse("${field.defaultValue?j_string?no_esc}", DateTimeFormatter.ofPattern("${field.formatValue?no_esc}")); 23 | <#elseif field.functionByType != "date" && field.functionByType != "datetime"> 24 | ${field.fieldType} ${field.name} = "${field.defaultValue}"; 25 | 26 | <#else> 27 | <@writeComplexAttrField complexField=field/> 28 | 29 | 30 | 31 | <#function writeRightValue value type format=""> 32 | <#if value?is_number> 33 | <#return value?c/> 34 | <#elseif value?is_boolean> 35 | <#return value?then('true', 'false')/> 36 | <#elseif type == "dateValue"> 37 | <#return 'Date.from(Instant.parse(\"${value?datetime?string.iso}\"))'?no_esc/> 38 | <#elseif type == "datetimeValue" && format?has_content> 39 | <#return 'ZonedDateTime.parse("${value?datetime}", DateTimeFormatter.ofPattern("${format?no_esc}")'?no_esc/> 40 | <#elseif type != "dateValue" && type != "datetimeValue"> 41 | <#return '\"${value?j_string}\"'?no_esc/> 42 | 43 | 44 | 45 | <#macro writeSetterField field builderName> 46 | <#if field.functionByType??> 47 | <#if field.fieldType == "char"> 48 | public ${builderName}Builder set${field.name?cap_first}(final String ${field.name}) { 49 | <#else> 50 | public ${builderName}Builder set${field.name?cap_first}(final ${field.fieldType?no_esc} ${field.name}) { 51 | 52 | <#elseif field.complexType??> 53 | <#if field.complexType.name() == "COLLECTION"> 54 | public ${builderName}Builder set${field.name?cap_first}(final ${field.fieldType?no_esc} ${field.name}) { 55 | <#elseif field.needBuilder> 56 | public ${builderName}Builder set${field.name?cap_first}(final ${field.fieldType?no_esc}Builder ${field.name}) { 57 | <#else > 58 | public ${builderName}Builder set${field.name?cap_first}(final ${field.fieldType?no_esc} ${field.name}) { 59 | 60 | <#else> 61 | public ${builderName}Builder set${field.name?cap_first}(final ${field.fieldType?no_esc} ${field.name}) { 62 | 63 | this.${field.name} = ${field.name}; 64 | return this; 65 | } 66 | 67 | 68 | <#macro writeExpectedInstanceSetters field> 69 | <#if !field.empty> 70 | <#if field.fieldType == "char"> 71 | object.set${field.name?cap_first}(this.${field.name}.charAt(0)); 72 | <#else> 73 | object.set${field.name?cap_first}(this.${field.name}); 74 | 75 | 76 | 77 | 78 | <#function writeDefaultsList fieldList> 79 | <#list fieldList as internalField> 80 | ${writeRightValue(internalField.defaultValue, internalField.functionByType, internalField.format)}<#if internalField?is_last>, 81 | 82 | 83 | 84 | <#macro writeComplexAttrField complexField> 85 | <#if complexField.empty> 86 | <#-- Empty field there is the need to be initialized --> 87 | <#elseif complexField.complexType.name() == "COLLECTION"> 88 | ${complexField.fieldType?no_esc} ${complexField.name} = List.of(${writeDefaultsList(complexField.fields)}); 89 | <#elseif complexField.needBuilder> 90 | ${complexField.fieldType?no_esc}Builder ${complexField.name} = new ${complexField.fieldType?no_esc}Builder(); 91 | <#else > 92 | ${complexField.fieldType?no_esc} ${complexField.name}; 93 | 94 | 95 | 96 | <#macro writeBuilderField field> 97 | <#if !field.empty> 98 | <#if field.functionByType??> 99 | <#if field.name?has_content> 100 | if (Objects.nonNull(${field.name})) { 101 | <#if field.formatValue??> 102 | <#if field.functionByType != "datetime"> 103 | pactDslJsonBody.${field.functionByType}("${field.name}", "${field.formatValue?no_esc}", ${field.name}); 104 | <#else> 105 | pactDslJsonBody.${field.functionByType}("${field.name}", "${field.formatValue?no_esc}", ${field.name}.toInstant()); 106 | 107 | <#elseif field.fieldType == "short"> 108 | pactDslJsonBody.${field.functionByType}("${field.name}", (int) ${field.name}); 109 | <#elseif field.fieldType == "BigInteger"> 110 | pactDslJsonBody.${field.functionByType}("${field.name}", ${field.name}.intValue()); 111 | <#elseif field.fieldType == "float"> 112 | pactDslJsonBody.${field.functionByType}("${field.name}", (double) ${field.name}); 113 | <#elseif field.fieldType == "byte"> 114 | pactDslJsonBody.${field.functionByType}("${field.name}", (int) ${field.name}); 115 | <#else> 116 | pactDslJsonBody.${field.functionByType}("${field.name}", ${field.name}); 117 | 118 | } 119 | <#else> 120 | <#if field.formatValue??> 121 | <#if field.functionByType == "dateValue"> 122 | pactDslJsonBody.eachLike(PactDslJsonRootValue.date("${field.formatValue?no_esc}", Date.from(Instant.now()))); 123 | <#elseif field.functionByType == "datetimeValue"> 124 | pactDslJsonBody.eachLike(PactDslJsonRootValue.datetime("${field.formatValue?no_esc}", ZonedDateTime.now())); 125 | <#elseif field.functionByType == "integerValue"> 126 | pactDslJsonBody.eachLike(PactDslJsonRootValue.integerMatching("${field.formatValue?no_esc}", ${field.defaultValue?int}); 127 | <#elseif field.functionByType == "decimalValue"> 128 | pactDslJsonBody.eachLike(PactDslJsonRootValue.decimalMatching("${field.formatValue?no_esc}", ${field.defaultValue?float}); 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | <#macro writeBuilderLambdaField fieldList> 137 | PactDslJsonRootValue 138 | <#list fieldList as internalField> 139 | <#if internalField.functionByType??> 140 | <#if internalField.name?has_content> 141 | if (Objects.nonNull(${internalField.name})) { 142 | <#if internalField.formatValue??> 143 | <#if internalField.functionByType != "date" && internalField.functionByType != "datetime"> 144 | .${internalField.functionByType}("${internalField.name}", "${internalField.formatValue?no_esc}", ${internalField.name}) 145 | <#else> 146 | .${internalField.functionByType}("${internalField.name}", "${internalField.formatValue?no_esc}", ${internalField.name}.toInstant()) 147 | 148 | <#else> 149 | .${internalField.functionByType}("${internalField.name}", ${internalField.name}) 150 | 151 | } 152 | <#else> 153 | <#if internalField.formatValue??> 154 | <#if internalField.functionByType == "dateValue"> 155 | .date("${internalField.formatValue?no_esc}", ${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 156 | <#elseif internalField.functionByType == "datetimeValue"> 157 | .datetime("${internalField.formatValue?no_esc}", ${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 158 | <#elseif internalField.functionByType == "integerValue"> 159 | .integerType("${internalField.formatValue?no_esc}",${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 160 | <#elseif internalField.functionByType == "decimalValue"> 161 | .decimalType("${internalField.formatValue?no_esc}", ${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 162 | 163 | <#elseif internalField.defaultValue?is_number> 164 | <#if internalField.functionByType == "dateValue"> 165 | .date(${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 166 | <#elseif internalField.functionByType == "datetimeValue"> 167 | .datetime(${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 168 | <#elseif internalField.functionByType == "integerValue"> 169 | .integerType(${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 170 | <#elseif internalField.functionByType == "decimalValue"> 171 | .decimalType(${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 172 | 173 | <#elseif internalField.functionByType == "stringValue" > 174 | .stringType(${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 175 | <#else > 176 | .${internalField.functionByType}(${writeRightValue(internalField.defaultValue, internalField.functionByType)}) 177 | 178 | 179 | 180 | 181 | 182 | 183 | <#macro writeBuilderComplexField complexField> 184 | <#if !complexField.empty> 185 | <#if complexField.complexType.name() == "COLLECTION"> 186 | <#if complexField.fieldValidations??> 187 | <#if complexField.fieldValidations.randomSize??> 188 | <#assign randomSize = "${complexField.fieldValidations.randomSize}"/> 189 | <#else > 190 | <#assign randomSize = ""/> 191 | 192 | <#if complexField.fieldValidations.max??> 193 | <#assign max = "${complexField.fieldValidations.max}"/> 194 | <#else > 195 | <#assign max = ""/> 196 | 197 | <#if complexField.fieldValidations.min??> 198 | <#assign min = "${complexField.fieldValidations.min}"/> 199 | <#else > 200 | <#assign min = ""/> 201 | 202 | <#if max?has_content && min?has_content> 203 | if (${complexField.name}.isEmpty()) { 204 | pactDslJsonBody.minMaxArrayLike("${complexField.name}", ${min}, ${max}); 205 | } else { 206 | pactDslJsonBody.minMaxArrayLike("${complexField.name}", ${min}, ${max}, <@writeBuilderLambdaField fieldList=complexField.fields/>, ${randomSize}); 207 | } 208 | <#elseif min?has_content> 209 | if (${complexField.name}.isEmpty()) { 210 | pactDslJsonBody.minArrayLike("${complexField.name}", ${min}); 211 | } else { 212 | pactDslJsonBody.minArrayLike("${complexField.name}", ${min}, <@writeBuilderLambdaField fieldList=complexField.fields/>, ${randomSize}); 213 | } 214 | <#elseif max?has_content> 215 | if (${complexField.name}.isEmpty()) { 216 | pactDslJsonBody.maxArrayLike("${complexField.name}", ${max}); 217 | } else { 218 | pactDslJsonBody.maxArrayLike("${complexField.name}", ${max}, <@writeBuilderLambdaField fieldList=complexField.fields/>, ${randomSize}); 219 | } 220 | <#elseif complexField.empty> 221 | pactDslJsonBody.eachLike("${complexField.name}"); 222 | <#else> 223 | pactDslJsonBody.eachLike("${complexField.name}", <@writeBuilderLambdaField fieldList=complexField.fields/>, 1); 224 | 225 | <#else> 226 | pactDslJsonBody.array("${complexField.name}"); 227 | <#list complexField.fields as internalField> 228 | <@writeBuilderField field=internalField/> 229 | 230 | pactDslJsonBody.closeArray(); 231 | 232 | <#elseif complexField.needBuilder> 233 | pactDslJsonBody.object("${complexField.name}", ${complexField.name}.build()); 234 | pactDslJsonBody.closeObject(); 235 | <#else> 236 | <@writeBuilderField field=complexField/> 237 | 238 | 239 | 240 | 241 | package ${schema.modelPackage}; 242 | 243 | import java.math.BigDecimal; 244 | import java.math.BigInteger; 245 | import java.text.DateFormat; 246 | import java.text.ParseException; 247 | import java.text.SimpleDateFormat; 248 | import java.time.Instant; 249 | import java.time.LocalDate; 250 | import java.time.ZonedDateTime; 251 | import java.time.format.DateTimeFormatter; 252 | import java.util.Date; 253 | import java.util.List; 254 | import java.util.Objects; 255 | 256 | import au.com.dius.pact.consumer.dsl.DslPart; 257 | import au.com.dius.pact.consumer.dsl.PactDslJsonArray; 258 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 259 | import au.com.dius.pact.consumer.dsl.PactDslJsonRootValue; 260 | <#if schema.customModifiers??> 261 | import com.sngular.annotation.processor.mapping.CustomDslModifier; 262 | 263 | 264 | 265 | import ${schema.modelPackage}.${schema.className}; 266 | 267 | <#function complex(field)> 268 | <#return field.functionByType??> 269 | 270 | 271 | <#function nocomplex(field)> 272 | <#if field.functionByType??> 273 | <#return false> 274 | <#else> 275 | <#return true> 276 | 277 | 278 | 279 | public class ${schema.className}Builder { 280 | <#list schema.fieldList as field> 281 | <#if !field.empty> 282 | <@writeAttrField field=field/> 283 | 284 | 285 | 286 | <#if constructor == true> 287 | public ${schema.className}Builder() throws ParseException {} 288 | 289 | 290 | <#list schema.fieldList as field> 291 | <#if !field.empty> 292 | <@writeSetterField field=field builderName=schema.className?cap_first/> 293 | 294 | 295 | 296 | public DslPart build() { 297 | PactDslJsonBody pactDslJsonBody = new PactDslJsonBody(); 298 | <#list schema.fieldList?filter(complex) as field> 299 | <@writeBuilderField field=field/> 300 | 301 | 302 | <#list schema.fieldList?filter(nocomplex) as field> 303 | <@writeBuilderComplexField complexField=field /> 304 | 305 | 306 | <#-- if schema.customModifiers??> 307 | try { 308 | applyCustomModifiers(pactDslJsonBody); 309 | } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { 310 | throw new RuntimeException(e); 311 | } 312 | 313 | return pactDslJsonBody; 314 | } 315 | 316 | public ${schema.className} buildExpectedInstance() { 317 | ${schema.className} object = new ${schema.className}(); 318 | <#list schema.fieldList as field> 319 | <@writeExpectedInstanceSetters field=field/> 320 | 321 | return object; 322 | } 323 | 324 | <#if schema.customModifiers??> 325 | private static void applyCustomModifiers(PactDslJsonBody pactDslJsonBody) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 326 | <#list schema.customModifiers as customMod> 327 | ((CustomDslModifier) Class.forName("${customMod}").newInstance()).apply(pactDslJsonBody); 328 | 329 | } 330 | 331 | 332 | } 333 | 334 | -------------------------------------------------------------------------------- /src/test/java/com/sngular/annotation/processor/BasicDataTypeTest.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.processor; 2 | 3 | import static com.google.testing.compile.CompilationSubject.assertThat; 4 | import static com.google.testing.compile.Compiler.javac; 5 | 6 | import com.google.testing.compile.Compilation; 7 | import com.google.testing.compile.JavaFileObjects; 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class BasicDataTypeTest { 11 | 12 | @Test 13 | public void characters() { 14 | 15 | Compilation compilation = javac().withProcessors(new PactDslProcessor()).compile(JavaFileObjects.forResource("basic/CharacterDataTypes.java")); 16 | assertThat(compilation).succeeded(); 17 | assertThat(compilation).generatedSourceFile("com/sngular/resources/basic/CharacterDataTypesBuilder") 18 | .hasSourceEquivalentTo(JavaFileObjects.forResource("basic/CharacterDataTypesBuilder.java")); 19 | } 20 | 21 | @Test 22 | public void numbers() { 23 | 24 | Compilation compilation = javac().withProcessors(new PactDslProcessor()).compile(JavaFileObjects.forResource("basic/NumericDataTypes.java")); 25 | assertThat(compilation).succeeded(); 26 | assertThat(compilation).generatedSourceFile("com/sngular/resources/basic/NumericDataTypesBuilder") 27 | .hasSourceEquivalentTo(JavaFileObjects.forResource("basic/NumericDataTypesBuilder.java")); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/sngular/annotation/processor/DateFormatTest.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.processor; 2 | 3 | import com.google.testing.compile.Compilation; 4 | import com.google.testing.compile.CompilationSubject; 5 | import com.google.testing.compile.Compiler; 6 | import com.google.testing.compile.JavaFileObjects; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class DateFormatTest { 10 | 11 | @Test 12 | public void basicDatesUsage() { 13 | 14 | Compilation compilation = Compiler.javac().withProcessors(new PactDslProcessor()).compile(JavaFileObjects.forResource("date/DateDataTypes.java")); 15 | CompilationSubject.assertThat(compilation).succeeded(); 16 | CompilationSubject.assertThat(compilation).generatedSourceFile("com/sngular/annotation/examples/DateDataTypesBuilder") 17 | .hasSourceEquivalentTo(JavaFileObjects.forResource("date/DateDataTypesBuilder.java")); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/sngular/annotation/processor/RandomValueGenerationTest.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.processor; 2 | 3 | import com.google.testing.compile.Compilation; 4 | import com.google.testing.compile.CompilationSubject; 5 | import com.google.testing.compile.Compiler; 6 | import com.google.testing.compile.JavaFileObjects; 7 | import org.apache.commons.rng.RestorableUniformRandomProvider; 8 | import org.junit.jupiter.api.Test; 9 | import org.mockito.Mockito; 10 | 11 | public class RandomValueGenerationTest { 12 | 13 | @Test 14 | public void numericDataTypes() { 15 | var theMock = Mockito.mock(RestorableUniformRandomProvider.class); 16 | Mockito.when(theMock.nextInt(0, Integer.MAX_VALUE)).thenReturn(18); 17 | Compilation compilation = Compiler.javac().withProcessors( new PactDslProcessor(theMock)).compile(JavaFileObjects.forResource("random/NumericDataTypes.java")); 18 | CompilationSubject.assertThat(compilation).succeeded(); 19 | CompilationSubject.assertThat(compilation).generatedSourceFile("com/sngular/annotation/examples/NumericDataTypesBuilder") 20 | .hasSourceEquivalentTo(JavaFileObjects.forResource("random/NumericDataTypesBuilder.java")); 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/basic/CharacterDataTypes.java: -------------------------------------------------------------------------------- 1 | package com.sngular.resources.basic; 2 | 3 | import com.sngular.annotation.pact.Example; 4 | import com.sngular.annotation.pact.PactDslBodyBuilder; 5 | 6 | @PactDslBodyBuilder 7 | public class CharacterDataTypes { 8 | 9 | @Example("nameExample") 10 | private String name; 11 | 12 | @Example("a") 13 | private char primitiveChar; 14 | 15 | @Example("b") 16 | private Character charObject; 17 | 18 | @Example("true") 19 | private boolean primitiveBoolean; 20 | 21 | @Example("false") 22 | private Boolean booleanObject; 23 | 24 | protected String getName() { 25 | return name; 26 | } 27 | 28 | protected void setName(final String name) { 29 | this.name = name; 30 | } 31 | 32 | public char getPrimitiveChar() { 33 | return primitiveChar; 34 | } 35 | 36 | public void setPrimitiveChar(char primitiveChar) { 37 | this.primitiveChar = primitiveChar; 38 | } 39 | 40 | public Character getCharObject() { 41 | return charObject; 42 | } 43 | 44 | public void setCharObject(Character charObject) { 45 | this.charObject = charObject; 46 | } 47 | 48 | public boolean isPrimitiveBoolean() { 49 | return primitiveBoolean; 50 | } 51 | 52 | public void setPrimitiveBoolean(boolean primitiveBoolean) { 53 | this.primitiveBoolean = primitiveBoolean; 54 | } 55 | 56 | public Boolean getBooleanObject() { 57 | return booleanObject; 58 | } 59 | 60 | public void setBooleanObject(Boolean booleanObject) { 61 | this.booleanObject = booleanObject; 62 | } 63 | } -------------------------------------------------------------------------------- /src/test/resources/basic/CharacterDataTypesBuilder.java: -------------------------------------------------------------------------------- 1 | package com.sngular.resources.basic; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.time.Instant; 9 | import java.time.LocalDate; 10 | import java.time.ZonedDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | import java.util.Date; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | import au.com.dius.pact.consumer.dsl.DslPart; 17 | import au.com.dius.pact.consumer.dsl.PactDslJsonArray; 18 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 19 | import au.com.dius.pact.consumer.dsl.PactDslJsonRootValue; 20 | import com.sngular.annotation.processor.mapping.CustomDslModifier; 21 | import com.sngular.resources.basic.CharacterDataTypes; 22 | 23 | public class CharacterDataTypesBuilder { 24 | String name = "nameExample"; 25 | 26 | String primitiveChar = "a"; 27 | 28 | String charObject = "b"; 29 | 30 | boolean primitiveBoolean = true; 31 | 32 | boolean booleanObject = false; 33 | 34 | public CharacterDataTypesBuilder setName(final String name) { 35 | this.name = name; 36 | return this; 37 | } 38 | 39 | public CharacterDataTypesBuilder setPrimitiveChar(final String primitiveChar) { 40 | this.primitiveChar = primitiveChar; 41 | return this; 42 | } 43 | 44 | public CharacterDataTypesBuilder setCharObject(final String charObject) { 45 | this.charObject = charObject; 46 | return this; 47 | } 48 | 49 | public CharacterDataTypesBuilder setPrimitiveBoolean(final boolean primitiveBoolean) { 50 | this.primitiveBoolean = primitiveBoolean; 51 | return this; 52 | } 53 | 54 | public CharacterDataTypesBuilder setBooleanObject(final boolean booleanObject) { 55 | this.booleanObject = booleanObject; 56 | return this; 57 | } 58 | 59 | public DslPart build() { 60 | PactDslJsonBody pactDslJsonBody = new PactDslJsonBody(); 61 | if (Objects.nonNull(name)) { 62 | pactDslJsonBody.stringType("name", name); 63 | } 64 | 65 | if (Objects.nonNull(primitiveChar)) { 66 | pactDslJsonBody.stringType("primitiveChar", primitiveChar); 67 | } 68 | 69 | if (Objects.nonNull(charObject)) { 70 | pactDslJsonBody.stringType("charObject", charObject); 71 | } 72 | 73 | if (Objects.nonNull(primitiveBoolean)) { 74 | pactDslJsonBody.booleanType("primitiveBoolean", primitiveBoolean); 75 | } 76 | 77 | if (Objects.nonNull(booleanObject)) { 78 | pactDslJsonBody.booleanType("booleanObject", booleanObject); 79 | } 80 | 81 | return pactDslJsonBody; 82 | } 83 | 84 | public CharacterDataTypes buildExpectedInstance() { 85 | CharacterDataTypes object = new CharacterDataTypes(); 86 | object.setName(this.name); 87 | object.setPrimitiveChar(this.primitiveChar.charAt(0)); 88 | object.setCharObject(this.charObject.charAt(0)); 89 | object.setPrimitiveBoolean(this.primitiveBoolean); 90 | object.setBooleanObject(this.booleanObject); 91 | return object; 92 | } 93 | 94 | private static void applyCustomModifiers(PactDslJsonBody pactDslJsonBody) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /src/test/resources/basic/NumericDataTypes.java: -------------------------------------------------------------------------------- 1 | package com.sngular.resources.basic; 2 | 3 | import com.sngular.annotation.pact.Example; 4 | import com.sngular.annotation.pact.PactDslBodyBuilder; 5 | 6 | import java.math.BigInteger; 7 | import java.math.BigDecimal; 8 | 9 | @PactDslBodyBuilder 10 | public class NumericDataTypes { 11 | 12 | @Example("10") 13 | private Integer integer; 14 | 15 | @Example("20") 16 | private int primitiveInt; 17 | 18 | @Example("30") 19 | private Long longObject; 20 | 21 | @Example("40") 22 | private long primitiveLong; 23 | 24 | @Example("50.1234") 25 | private Float floatObject; 26 | 27 | @Example("60.2345") 28 | private float primitiveFloat; 29 | 30 | @Example("70.3456") 31 | private Double doubleObject; 32 | 33 | @Example("80.4567") 34 | private double primitiveDouble; 35 | 36 | @Example("90") 37 | private Short shortObject; 38 | 39 | @Example("100") 40 | private short primitiveShort; 41 | 42 | @Example("110") 43 | private Byte byteObject; 44 | 45 | @Example("120") 46 | private byte primitiveByte; 47 | 48 | @Example("1303812548123548216") 49 | private BigInteger bigIntegerObject; 50 | 51 | @Example("1402354872534672834.2345") 52 | private BigDecimal bigDecimalObject; 53 | 54 | protected Integer getInteger() { 55 | return integer; 56 | } 57 | 58 | protected void setInteger(final Integer integer) { 59 | this.integer = integer; 60 | } 61 | 62 | protected int getPrimitiveInt() { 63 | return primitiveInt; 64 | } 65 | 66 | protected void setPrimitiveInt(final int primitiveInt) { 67 | this.primitiveInt = primitiveInt; 68 | } 69 | 70 | protected Long getLongObject() { 71 | return longObject; 72 | } 73 | 74 | protected void setLongObject(final Long longObject) { 75 | this.longObject = longObject; 76 | } 77 | 78 | protected long getPrimitiveLong() { 79 | return primitiveLong; 80 | } 81 | 82 | protected void setPrimitiveLong(final long primitiveLong) { 83 | this.primitiveLong = primitiveLong; 84 | } 85 | 86 | public Float getFloatObject() { 87 | return floatObject; 88 | } 89 | 90 | public void setFloatObject(Float floatObject) { 91 | this.floatObject = floatObject; 92 | } 93 | 94 | public float getPrimitiveFloat() { 95 | return primitiveFloat; 96 | } 97 | 98 | public void setPrimitiveFloat(float primitiveFloat) { 99 | this.primitiveFloat = primitiveFloat; 100 | } 101 | 102 | public Double getDoubleObject() { 103 | return doubleObject; 104 | } 105 | 106 | public void setDoubleObject(Double doubleObject) { 107 | this.doubleObject = doubleObject; 108 | } 109 | 110 | public double getPrimitiveDouble() { 111 | return primitiveDouble; 112 | } 113 | 114 | public void setPrimitiveDouble(double primitiveDouble) { 115 | this.primitiveDouble = primitiveDouble; 116 | } 117 | 118 | public Short getShortObject() { 119 | return shortObject; 120 | } 121 | 122 | public void setShortObject(Short shortObject) { 123 | this.shortObject = shortObject; 124 | } 125 | 126 | public short getPrimitiveShort() { 127 | return primitiveShort; 128 | } 129 | 130 | public void setPrimitiveShort(short primitiveShort) { 131 | this.primitiveShort = primitiveShort; 132 | } 133 | 134 | public Byte getByteObject() { 135 | return byteObject; 136 | } 137 | 138 | public void setByteObject(Byte byteObject) { 139 | this.byteObject = byteObject; 140 | } 141 | 142 | public byte getPrimitiveByte() { 143 | return primitiveByte; 144 | } 145 | 146 | public void setPrimitiveByte(byte primitiveByte) { 147 | this.primitiveByte = primitiveByte; 148 | } 149 | 150 | public BigInteger getBigIntegerObject() { 151 | return bigIntegerObject; 152 | } 153 | 154 | public void setBigIntegerObject(BigInteger bigIntegerObject) { 155 | this.bigIntegerObject = bigIntegerObject; 156 | } 157 | 158 | public BigDecimal getBigDecimalObject() { 159 | return bigDecimalObject; 160 | } 161 | 162 | public void setBigDecimalObject(BigDecimal bigDecimalObject) { 163 | this.bigDecimalObject = bigDecimalObject; 164 | } 165 | } -------------------------------------------------------------------------------- /src/test/resources/basic/NumericDataTypesBuilder.java: -------------------------------------------------------------------------------- 1 | package com.sngular.resources.basic; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.time.Instant; 9 | import java.time.LocalDate; 10 | import java.time.ZonedDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | import java.util.Date; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | import au.com.dius.pact.consumer.dsl.DslPart; 17 | import au.com.dius.pact.consumer.dsl.PactDslJsonArray; 18 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 19 | import au.com.dius.pact.consumer.dsl.PactDslJsonRootValue; 20 | import com.sngular.annotation.processor.mapping.CustomDslModifier; 21 | import com.sngular.resources.basic.NumericDataTypes; 22 | 23 | public class NumericDataTypesBuilder { 24 | 25 | int integer = 10; 26 | 27 | int primitiveInt = 20; 28 | 29 | long longObject = 30L; 30 | 31 | long primitiveLong = 40L; 32 | 33 | float floatObject = 50.1234F; 34 | 35 | float primitiveFloat = 60.2345F; 36 | 37 | double doubleObject = 70.3456D; 38 | 39 | double primitiveDouble = 80.4567D; 40 | 41 | short shortObject = 90; 42 | 43 | short primitiveShort = 100; 44 | 45 | byte byteObject = 110; 46 | 47 | byte primitiveByte = 120; 48 | 49 | BigInteger bigIntegerObject = new BigInteger("1303812548123548216"); 50 | 51 | BigDecimal bigDecimalObject = new BigDecimal("1402354872534672834.2345"); 52 | 53 | public NumericDataTypesBuilder setInteger(final int integer) { 54 | this.integer = integer; 55 | return this; 56 | } 57 | 58 | public NumericDataTypesBuilder setPrimitiveInt(final int primitiveInt) { 59 | this.primitiveInt = primitiveInt; 60 | return this; 61 | } 62 | 63 | public NumericDataTypesBuilder setLongObject(final long longObject) { 64 | this.longObject = longObject; 65 | return this; 66 | } 67 | 68 | public NumericDataTypesBuilder setPrimitiveLong(final long primitiveLong) { 69 | this.primitiveLong = primitiveLong; 70 | return this; 71 | } 72 | 73 | public NumericDataTypesBuilder setFloatObject(final float floatObject) { 74 | this.floatObject = floatObject; 75 | return this; 76 | } 77 | 78 | public NumericDataTypesBuilder setPrimitiveFloat(final float primitiveFloat) { 79 | this.primitiveFloat = primitiveFloat; 80 | return this; 81 | } 82 | 83 | public NumericDataTypesBuilder setDoubleObject(final double doubleObject) { 84 | this.doubleObject = doubleObject; 85 | return this; 86 | } 87 | 88 | public NumericDataTypesBuilder setPrimitiveDouble(final double primitiveDouble) { 89 | this.primitiveDouble = primitiveDouble; 90 | return this; 91 | } 92 | 93 | public NumericDataTypesBuilder setShortObject(final short shortObject) { 94 | this.shortObject = shortObject; 95 | return this; 96 | } 97 | 98 | public NumericDataTypesBuilder setPrimitiveShort(final short primitiveShort) { 99 | this.primitiveShort = primitiveShort; 100 | return this; 101 | } 102 | 103 | public NumericDataTypesBuilder setByteObject(final byte byteObject) { 104 | this.byteObject = byteObject; 105 | return this; 106 | } 107 | 108 | public NumericDataTypesBuilder setPrimitiveByte(final byte primitiveByte) { 109 | this.primitiveByte = primitiveByte; 110 | return this; 111 | } 112 | 113 | public NumericDataTypesBuilder setBigIntegerObject(final BigInteger bigIntegerObject) { 114 | this.bigIntegerObject = bigIntegerObject; 115 | return this; 116 | } 117 | 118 | public NumericDataTypesBuilder setBigDecimalObject(final BigDecimal bigDecimalObject) { 119 | this.bigDecimalObject = bigDecimalObject; 120 | return this; 121 | } 122 | 123 | public DslPart build() { 124 | PactDslJsonBody pactDslJsonBody = new PactDslJsonBody(); 125 | if (Objects.nonNull(integer)) { 126 | pactDslJsonBody.integerType("integer", integer); 127 | } 128 | if (Objects.nonNull(primitiveInt)) { 129 | pactDslJsonBody.integerType("primitiveInt", primitiveInt); 130 | } 131 | if (Objects.nonNull(longObject)) { 132 | pactDslJsonBody.integerType("longObject", longObject); 133 | } 134 | if (Objects.nonNull(primitiveLong)) { 135 | pactDslJsonBody.integerType("primitiveLong", primitiveLong); 136 | } 137 | if (Objects.nonNull(floatObject)) { 138 | pactDslJsonBody.decimalType("floatObject", (double) floatObject); 139 | } 140 | 141 | if (Objects.nonNull(primitiveFloat)) { 142 | pactDslJsonBody.decimalType("primitiveFloat", (double) primitiveFloat); 143 | } 144 | 145 | if (Objects.nonNull(doubleObject)) { 146 | pactDslJsonBody.decimalType("doubleObject", doubleObject); 147 | } 148 | 149 | if (Objects.nonNull(primitiveDouble)) { 150 | pactDslJsonBody.decimalType("primitiveDouble", primitiveDouble); 151 | } 152 | 153 | if (Objects.nonNull(shortObject)) { 154 | pactDslJsonBody.integerType("shortObject", (int) shortObject); 155 | } 156 | 157 | if (Objects.nonNull(primitiveShort)) { 158 | pactDslJsonBody.integerType("primitiveShort", (int) primitiveShort); 159 | } 160 | 161 | if (Objects.nonNull(byteObject)) { 162 | pactDslJsonBody.integerType("byteObject", (int) byteObject); 163 | } 164 | 165 | if (Objects.nonNull(primitiveByte)) { 166 | pactDslJsonBody.integerType("primitiveByte", (int) primitiveByte); 167 | } 168 | 169 | if (Objects.nonNull(bigIntegerObject)) { 170 | pactDslJsonBody.integerType("bigIntegerObject", bigIntegerObject.intValue()); 171 | } 172 | 173 | if (Objects.nonNull(bigDecimalObject)) { 174 | pactDslJsonBody.decimalType("bigDecimalObject", bigDecimalObject); 175 | } 176 | 177 | return pactDslJsonBody; 178 | } 179 | 180 | public NumericDataTypes buildExpectedInstance() { 181 | NumericDataTypes object = new NumericDataTypes(); 182 | object.setInteger(this.integer); 183 | object.setPrimitiveInt(this.primitiveInt); 184 | object.setLongObject(this.longObject); 185 | object.setPrimitiveLong(this.primitiveLong); 186 | object.setFloatObject(this.floatObject); 187 | object.setPrimitiveFloat(this.primitiveFloat); 188 | object.setDoubleObject(this.doubleObject); 189 | object.setPrimitiveDouble(this.primitiveDouble); 190 | object.setShortObject(this.shortObject); 191 | object.setPrimitiveShort(this.primitiveShort); 192 | object.setByteObject(this.byteObject); 193 | object.setPrimitiveByte(this.primitiveByte); 194 | object.setBigIntegerObject(this.bigIntegerObject); 195 | object.setBigDecimalObject(this.bigDecimalObject); 196 | 197 | return object; 198 | } 199 | private static void applyCustomModifiers(PactDslJsonBody pactDslJsonBody) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 200 | } 201 | } -------------------------------------------------------------------------------- /src/test/resources/complex/NestedObjects.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sngular/pact-annotation-processor/5e906dbf9880f71663f74130319309772f070746/src/test/resources/complex/NestedObjects.java -------------------------------------------------------------------------------- /src/test/resources/date/DateDataTypes.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.examples; 2 | 3 | import java.util.Date; 4 | import java.time.ZonedDateTime; 5 | 6 | import com.sngular.annotation.pact.Example; 7 | import com.sngular.annotation.pact.PactDslBodyBuilder; 8 | 9 | @PactDslBodyBuilder 10 | public class DateDataTypes { 11 | 12 | @Example(value = "23/04/2023 12:00", format = "dd/MM/yyyy HH:mm") 13 | private ZonedDateTime formattedZoneDateTime; 14 | 15 | @Example(value = "2023-12-03T10:15:30+01:00[Europe/Madrid]") 16 | private ZonedDateTime defaultFormatZoneDateTime; 17 | 18 | @Example(value = "2023-04-23") 19 | private Date defaultFormatDate; 20 | 21 | @Example(value = "23-04-2023", format = "dd-MM-yyyy") 22 | private Date formattedDate; 23 | 24 | protected ZonedDateTime getFormattedZoneDateTime() { 25 | return formattedZoneDateTime; 26 | } 27 | 28 | protected void setFormattedZoneDateTime(final ZonedDateTime formattedZoneDateTime) { 29 | this.formattedZoneDateTime = formattedZoneDateTime; 30 | } 31 | 32 | protected ZonedDateTime getDefaultFormatZoneDateTime() { 33 | return defaultFormatZoneDateTime; 34 | } 35 | 36 | protected void setDefaultFormatZoneDateTime(final ZonedDateTime defaultFormatZoneDateTime) { 37 | this.defaultFormatZoneDateTime = defaultFormatZoneDateTime; 38 | } 39 | 40 | protected Date getDefaultFormatDate() { 41 | return defaultFormatDate; 42 | } 43 | 44 | protected void setDefaultFormatDate(final Date defaultFormatDate) { 45 | this.defaultFormatDate = defaultFormatDate; 46 | } 47 | 48 | protected Date getFormattedDate() { 49 | return formattedDate; 50 | } 51 | 52 | protected void setFormattedDate(final Date formattedDate) { 53 | this.formattedDate = formattedDate; 54 | } 55 | } -------------------------------------------------------------------------------- /src/test/resources/date/DateDataTypesBuilder.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.examples; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.time.Instant; 9 | import java.time.LocalDate; 10 | import java.time.ZonedDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | import java.util.Date; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | import au.com.dius.pact.consumer.dsl.DslPart; 17 | import au.com.dius.pact.consumer.dsl.PactDslJsonArray; 18 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 19 | import au.com.dius.pact.consumer.dsl.PactDslJsonRootValue; 20 | import com.sngular.annotation.processor.mapping.CustomDslModifier; 21 | import com.sngular.annotation.examples.DateDataTypes; 22 | 23 | 24 | public class DateDataTypesBuilder { 25 | ZonedDateTime formattedZoneDateTime = ZonedDateTime.parse("23/04/2023 12:00", DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")); 26 | ZonedDateTime defaultFormatZoneDateTime = ZonedDateTime.parse("2023-12-03T10:15:30+01:00[Europe/Madrid]", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSS]XXX['['VV']']")); 27 | Date defaultFormatDate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-04-23"); 28 | Date formattedDate = new SimpleDateFormat("dd-MM-yyyy").parse("23-04-2023"); 29 | 30 | public DateDataTypesBuilder() throws ParseException {} 31 | 32 | public DateDataTypesBuilder setFormattedZoneDateTime(final ZonedDateTime formattedZoneDateTime) { 33 | this.formattedZoneDateTime = formattedZoneDateTime; 34 | return this; 35 | } 36 | public DateDataTypesBuilder setDefaultFormatZoneDateTime(final ZonedDateTime defaultFormatZoneDateTime) { 37 | this.defaultFormatZoneDateTime = defaultFormatZoneDateTime; 38 | return this; 39 | } 40 | public DateDataTypesBuilder setDefaultFormatDate(final Date defaultFormatDate) { 41 | this.defaultFormatDate = defaultFormatDate; 42 | return this; 43 | } 44 | public DateDataTypesBuilder setFormattedDate(final Date formattedDate) { 45 | this.formattedDate = formattedDate; 46 | return this; 47 | } 48 | 49 | public DslPart build() { 50 | PactDslJsonBody pactDslJsonBody = new PactDslJsonBody(); 51 | if (Objects.nonNull(formattedZoneDateTime)) { 52 | pactDslJsonBody.datetime("formattedZoneDateTime", "dd/MM/yyyy HH:mm", formattedZoneDateTime.toInstant()); 53 | } 54 | if (Objects.nonNull(defaultFormatZoneDateTime)) { 55 | pactDslJsonBody.datetime("defaultFormatZoneDateTime", "yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSS]XXX['['VV']']", defaultFormatZoneDateTime.toInstant()); 56 | } 57 | if (Objects.nonNull(defaultFormatDate)) { 58 | pactDslJsonBody.date("defaultFormatDate", "yyyy-MM-dd", defaultFormatDate); 59 | } 60 | if (Objects.nonNull(formattedDate)) { 61 | pactDslJsonBody.date("formattedDate", "dd-MM-yyyy", formattedDate); 62 | } 63 | 64 | return pactDslJsonBody; 65 | } 66 | public DateDataTypes buildExpectedInstance() { 67 | DateDataTypes object = new DateDataTypes(); 68 | object.setFormattedZoneDateTime(this.formattedZoneDateTime); 69 | object.setDefaultFormatZoneDateTime(this.defaultFormatZoneDateTime); 70 | object.setDefaultFormatDate(this.defaultFormatDate); 71 | object.setFormattedDate(this.formattedDate); 72 | return object; 73 | } 74 | private static void applyCustomModifiers(PactDslJsonBody pactDslJsonBody) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 75 | } 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/test/resources/random/NumericDataTypes.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.examples; 2 | 3 | import com.sngular.annotation.pact.Example; 4 | import com.sngular.annotation.pact.PactDslBodyBuilder; 5 | 6 | @PactDslBodyBuilder 7 | public class NumericDataTypes { 8 | 9 | private int age; 10 | 11 | protected int getAge() { 12 | return age; 13 | } 14 | 15 | protected void setAge(final int age) { 16 | this.age = age; 17 | } 18 | } -------------------------------------------------------------------------------- /src/test/resources/random/NumericDataTypesBuilder.java: -------------------------------------------------------------------------------- 1 | package com.sngular.annotation.examples; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.time.Instant; 9 | import java.time.LocalDate; 10 | import java.time.ZonedDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | import java.util.Date; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | import au.com.dius.pact.consumer.dsl.DslPart; 17 | import au.com.dius.pact.consumer.dsl.PactDslJsonArray; 18 | import au.com.dius.pact.consumer.dsl.PactDslJsonBody; 19 | import au.com.dius.pact.consumer.dsl.PactDslJsonRootValue; 20 | import com.sngular.annotation.processor.mapping.CustomDslModifier; 21 | import com.sngular.annotation.examples.NumericDataTypes; 22 | 23 | public class NumericDataTypesBuilder { 24 | int age = 0; 25 | 26 | public NumericDataTypesBuilder setAge(final int age) { 27 | this.age = age; 28 | return this; 29 | } 30 | 31 | public DslPart build() { 32 | PactDslJsonBody pactDslJsonBody = new PactDslJsonBody(); 33 | if (Objects.nonNull(age)) { 34 | pactDslJsonBody.integerType("age", age); 35 | } 36 | 37 | return pactDslJsonBody; 38 | } 39 | public NumericDataTypes buildExpectedInstance() { 40 | NumericDataTypes object = new NumericDataTypes(); 41 | object.setAge(this.age); 42 | return object; 43 | } 44 | private static void applyCustomModifiers(PactDslJsonBody pactDslJsonBody) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /styles/OS3_Style_idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | --------------------------------------------------------------------------------