diff --git a/.github/actions/await-http-resource/action.yml b/.github/actions/await-http-resource/action.yml deleted file mode 100644 index ba177fb75..000000000 --- a/.github/actions/await-http-resource/action.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Await HTTP Resource -description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)' -inputs: - url: - description: 'URL of the resource to await' - required: true -runs: - using: composite - steps: - - name: Await HTTP resource - shell: bash - run: | - url=${{ inputs.url }} - echo "Waiting for $url" - until curl --fail --head --silent ${{ inputs.url }} > /dev/null - do - echo "." - sleep 60 - done - echo "$url is available" diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml deleted file mode 100644 index 70b051310..000000000 --- a/.github/actions/build/action.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Build -description: 'Builds the project, optionally publishing it to a local deployment repository' -inputs: - develocity-access-key: - description: 'Access key for authentication with ge.spring.io' - required: false - gradle-cache-read-only: - description: 'Whether Gradle''s cache should be read only' - required: false - default: 'true' - java-distribution: - description: 'Java distribution to use' - required: false - default: 'liberica' - java-early-access: - description: 'Whether the Java version is in early access' - required: false - default: 'false' - java-toolchain: - description: 'Whether a Java toolchain should be used' - required: false - default: 'false' - java-version: - description: 'Java version to compile and test with' - required: false - default: '17' - publish: - description: 'Whether to publish artifacts ready for deployment to Artifactory' - required: false - default: 'false' -outputs: - build-scan-url: - description: 'URL, if any, of the build scan produced by the build' - value: ${{ (inputs.publish == 'true' && steps.publish.outputs.build-scan-url) || steps.build.outputs.build-scan-url }} - version: - description: 'Version that was built' - value: ${{ steps.read-version.outputs.version }} -runs: - using: composite - steps: - - name: Prepare Gradle Build - uses: ./.github/actions/prepare-gradle-build - with: - cache-read-only: ${{ inputs.gradle-cache-read-only }} - develocity-access-key: ${{ inputs.develocity-access-key }} - java-distribution: ${{ inputs.java-distribution }} - java-early-access: ${{ inputs.java-early-access }} - java-toolchain: ${{ inputs.java-toolchain }} - java-version: ${{ inputs.java-version }} - - name: Build - id: build - if: ${{ inputs.publish == 'false' }} - shell: bash - run: ./gradlew build - - name: Publish - id: publish - if: ${{ inputs.publish == 'true' }} - shell: bash - run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository ${{ !startsWith(github.event.head_commit.message, 'Next development version') && 'build' || '' }} publishAllPublicationsToDeploymentRepository - - name: Read Version From gradle.properties - id: read-version - shell: bash - run: | - version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties) - echo "Version is $version" - echo "version=$version" >> $GITHUB_OUTPUT diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml deleted file mode 100644 index 03452537a..000000000 --- a/.github/actions/create-github-release/action.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Create GitHub Release -description: 'Create the release on GitHub with a changelog' -inputs: - milestone: - description: 'Name of the GitHub milestone for which a release will be created' - required: true - pre-release: - description: 'Whether the release is a pre-release (a milestone or release candidate)' - required: false - default: 'false' - token: - description: 'Token to use for authentication with GitHub' - required: true -runs: - using: composite - steps: - - name: Generate Changelog - uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11 - with: - config-file: .github/actions/create-github-release/changelog-generator.yml - milestone: ${{ inputs.milestone }} - token: ${{ inputs.token }} - - name: Create GitHub Release - shell: bash - env: - GITHUB_TOKEN: ${{ inputs.token }} - run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || '' }} diff --git a/.github/actions/create-github-release/changelog-generator.yml b/.github/actions/create-github-release/changelog-generator.yml deleted file mode 100644 index 0c1077fdf..000000000 --- a/.github/actions/create-github-release/changelog-generator.yml +++ /dev/null @@ -1,22 +0,0 @@ -changelog: - sections: - - title: ":star: New Features" - labels: - - "type: enhancement" - - title: ":lady_beetle: Bug Fixes" - labels: - - "type: bug" - - "type: regression" - - title: ":notebook_with_decorative_cover: Documentation" - labels: - - "type: documentation" - - title: ":hammer: Dependency Upgrades" - sort: "title" - labels: - - "type: dependency-upgrade" - issues: - ports: - - label: "status: forward-port" - bodyExpression: 'Forward port of issue #(\d+).*' - - label: "status: back-port" - bodyExpression: 'Back port of issue #(\d+).*' diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml deleted file mode 100644 index f9969cf31..000000000 --- a/.github/actions/prepare-gradle-build/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Prepare Gradle Build -description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties' -inputs: - cache-read-only: - description: 'Whether Gradle''s cache should be read only' - required: false - default: 'true' - develocity-access-key: - description: 'Access key for authentication with ge.spring.io' - required: false - java-distribution: - description: 'Java distribution to use' - required: false - default: 'liberica' - java-early-access: - description: 'Whether the Java version is in early access. When true, forces java-distribution to temurin' - required: false - default: 'false' - java-toolchain: - description: 'Whether a Java toolchain should be used' - required: false - default: 'false' - java-version: - description: 'Java version to use for the build' - required: false - default: '17' -runs: - using: composite - steps: - - name: Set Up Java - uses: actions/setup-java@v4 - with: - distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }} - java-version: | - ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} - ${{ inputs.java-toolchain == 'true' && '17' || '' }} - - name: Set Up Gradle With Read/Write Cache - if: ${{ inputs.cache-read-only == 'false' }} - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - with: - cache-read-only: false - develocity-access-key: ${{ inputs.develocity-access-key }} - - name: Set Up Gradle - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0 - with: - develocity-access-key: ${{ inputs.develocity-access-key }} - - name: Configure Gradle Properties - shell: bash - run: | - mkdir -p $HOME/.gradle - echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties - echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties - echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties - - name: Configure Toolchain Properties - if: ${{ inputs.java-toolchain == 'true' }} - shell: bash - run: | - echo toolchainVersion=${{ inputs.java-version }} >> $HOME/.gradle/gradle.properties - echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties - echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties - echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $HOME/.gradle/gradle.properties diff --git a/.github/actions/print-jvm-thread-dumps/action.yml b/.github/actions/print-jvm-thread-dumps/action.yml deleted file mode 100644 index bcaebf367..000000000 --- a/.github/actions/print-jvm-thread-dumps/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Print JVM thread dumps -description: 'Prints a thread dump for all running JVMs' -runs: - using: composite - steps: - - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do - jcmd $jvm_pid Thread.print - done - - if: ${{ runner.os == 'Windows' }} - shell: powershell - run: | - foreach ($jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem)) { - jcmd $jvm_pid Thread.print - } diff --git a/.github/actions/send-notification/action.yml b/.github/actions/send-notification/action.yml deleted file mode 100644 index b379e6789..000000000 --- a/.github/actions/send-notification/action.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Send Notification -description: 'Sends a Google Chat message as a notification of the job''s outcome' -inputs: - build-scan-url: - description: 'URL of the build scan to include in the notification' - required: false - run-name: - description: 'Name of the run to include in the notification' - required: false - default: ${{ format('{0} {1}', github.ref_name, github.job) }} - status: - description: 'Status of the job' - required: true - webhook-url: - description: 'Google Chat Webhook URL' - required: true -runs: - using: composite - steps: - - name: Prepare Variables - shell: bash - run: | - echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV" - echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV" - - name: Success Notification - if: ${{ inputs.status == 'success' }} - shell: bash - run: | - curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true - - name: Failure Notification - if: ${{ inputs.status == 'failure' }} - shell: bash - run: | - curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: " *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true - - name: Cancel Notification - if: ${{ inputs.status == 'cancelled' }} - shell: bash - run: | - curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true diff --git a/.github/actions/sync-to-maven-central/action.yml b/.github/actions/sync-to-maven-central/action.yml deleted file mode 100644 index ec3b20d36..000000000 --- a/.github/actions/sync-to-maven-central/action.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Sync to Maven Central -description: 'Syncs a release to Maven Central and waits for it to be available for use' -inputs: - jfrog-cli-config-token: - description: 'Config token for the JFrog CLI' - required: true - ossrh-s01-staging-profile: - description: 'Staging profile to use when syncing to Central' - required: true - ossrh-s01-token-password: - description: 'Password for authentication with s01.oss.sonatype.org' - required: true - ossrh-s01-token-username: - description: 'Username for authentication with s01.oss.sonatype.org' - required: true - spring-restdocs-version: - description: 'Version of Spring REST Docs that is being synced to Central' - required: true -runs: - using: composite - steps: - - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 - env: - JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - - name: Download Release Artifacts - shell: bash - run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-restdocs-{0}', inputs.spring-restdocs-version) }};buildNumber=${{ github.run_number }}' - - name: Sync - uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1 - with: - close: true - create: true - generate-checksums: true - password: ${{ inputs.ossrh-s01-token-password }} - release: true - staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }} - upload: true - username: ${{ inputs.ossrh-s01-token-username }} - - name: Await - uses: ./.github/actions/await-http-resource - with: - url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/restdocs/spring-restdocs-core/{0}/spring-restdocs-core-{0}.jar', inputs.spring-restdocs-version) }} diff --git a/.github/actions/sync-to-maven-central/artifacts.spec b/.github/actions/sync-to-maven-central/artifacts.spec deleted file mode 100644 index 82049730c..000000000 --- a/.github/actions/sync-to-maven-central/artifacts.spec +++ /dev/null @@ -1,20 +0,0 @@ -{ - "files": [ - { - "aql": { - "items.find": { - "$and": [ - { - "@build.name": "${buildName}", - "@build.number": "${buildNumber}", - "path": { - "$nmatch": "org/springframework/restdocs/spring-restdocs/*" - } - } - ] - } - }, - "target": "nexus/" - } - ] -} diff --git a/.github/dco.yml b/.github/dco.yml deleted file mode 100644 index 0c4b142e9..000000000 --- a/.github/dco.yml +++ /dev/null @@ -1,2 +0,0 @@ -require: - members: false diff --git a/.github/workflows/build-and-deploy-snapshot.yml b/.github/workflows/build-and-deploy-snapshot.yml deleted file mode 100644 index 8850cec14..000000000 --- a/.github/workflows/build-and-deploy-snapshot.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build and Deploy Snapshot -on: - push: - branches: - - main -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} -jobs: - build-and-deploy-snapshot: - name: Build and Deploy Snapshot - if: ${{ github.repository == 'spring-projects/spring-restdocs' }} - runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@v4 - - name: Build and Publish - id: build-and-publish - uses: ./.github/actions/build - with: - develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - gradle-cache-read-only: false - publish: true - - name: Deploy - uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1 - with: - artifact-properties: | - /**/spring-restdocs-*.zip::zip.type=docs,zip.deployed=false - build-name: 'spring-restdocs-4.0.x' - folder: 'deployment-repository' - password: ${{ secrets.ARTIFACTORY_PASSWORD }} - repository: ${{ 'libs-snapshot-local' }} - signing-key: ${{ secrets.GPG_PRIVATE_KEY }} - signing-passphrase: ${{ secrets.GPG_PASSPHRASE }} - uri: 'https://repo.spring.io' - username: ${{ secrets.ARTIFACTORY_USERNAME }} - - name: Send Notification - if: always() - uses: ./.github/actions/send-notification - with: - build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }} - run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }} - status: ${{ job.status }} - webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} - outputs: - version: ${{ steps.build-and-publish.outputs.version }} diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml deleted file mode 100644 index 164c04dc2..000000000 --- a/.github/workflows/build-pull-request.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Build Pull Request -on: pull_request -permissions: - contents: read -jobs: - build: - name: Build Pull Request - if: ${{ github.repository == 'spring-projects/spring-restdocs' }} - runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@v4 - - name: Build - id: build - uses: ./.github/actions/build - - name: Print JVM Thread Dumps When Cancelled - if: cancelled() - uses: ./.github/actions/print-jvm-thread-dumps - - name: Upload Build Reports - if: failure() - uses: actions/upload-artifact@v4 - with: - name: build-reports - path: '**/build/reports/' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 4df789601..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: CI -on: - push: - branches: - - main -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} -jobs: - ci: - name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}' - if: ${{ github.repository == 'spring-projects/spring-restdocs' }} - runs-on: ${{ matrix.os.id }} - strategy: - fail-fast: false - matrix: - os: - - id: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} - name: Linux - - id: windows-latest - name: Windows - java: - - version: 17 - toolchain: false - - version: 21 - toolchain: false - - version: 24 - toolchain: false - exclude: - - os: - name: Linux - java: - version: 17 - steps: - - name: Prepare Windows runner - if: ${{ runner.os == 'Windows' }} - run: | - git config --global core.autocrlf true - git config --global core.longPaths true - Stop-Service -name Docker - - name: Check Out Code - uses: actions/checkout@v4 - - name: Build - id: build - uses: ./.github/actions/build - with: - develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - gradle-cache-read-only: false - java-early-access: ${{ matrix.java.early-access || 'false' }} - java-distribution: ${{ matrix.java.distribution }} - java-toolchain: ${{ matrix.java.toolchain }} - java-version: ${{ matrix.java.version }} - - name: Send Notification - if: always() - uses: ./.github/actions/send-notification - with: - build-scan-url: ${{ steps.build.outputs.build-scan-url }} - run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }} - status: ${{ job.status }} - webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} diff --git a/.github/workflows/delete-staged-release.yml b/.github/workflows/delete-staged-release.yml deleted file mode 100644 index 56aff2b40..000000000 --- a/.github/workflows/delete-staged-release.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Delete Staged Release -on: - workflow_dispatch: - inputs: - build-version: - description: 'Version of the build to delete' - required: true -jobs: - delete-staged-release: - name: Delete Staged Release - runs-on: ubuntu-latest - steps: - - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 - env: - JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }} - - name: Delete Build - run: jfrog rt delete --build spring-restdocs-${{ github.event.inputs.build-version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index ef92b5cb7..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Release -on: - push: - tags: - - v3.0.[0-9]+ -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} -jobs: - build-and-stage-release: - name: Build and Stage Release - if: ${{ github.repository == 'spring-projects/spring-restdocs' }} - runs-on: ${{ vars.UBUNTU_MEDIUIM || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@v4 - - name: Build and Publish - id: build-and-publish - uses: ./.github/actions/build - with: - develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - gradle-cache-read-only: false - publish: true - - name: Stage Release - uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1 - with: - artifact-properties: | - /**/spring-restdocs-*.zip::zip.type=docs,zip.deployed=false - build-name: ${{ format('spring-restdocs-{0}', steps.build-and-publish.outputs.version) }} - folder: 'deployment-repository' - password: ${{ secrets.ARTIFACTORY_PASSWORD }} - repository: 'libs-staging-local' - signing-key: ${{ secrets.GPG_PRIVATE_KEY }} - signing-passphrase: ${{ secrets.GPG_PASSPHRASE }} - uri: 'https://repo.spring.io' - username: ${{ secrets.ARTIFACTORY_USERNAME }} - outputs: - version: ${{ steps.build-and-publish.outputs.version }} - sync-to-maven-central: - name: Sync to Maven Central - needs: - - build-and-stage-release - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Sync to Maven Central - uses: ./.github/actions/sync-to-maven-central - with: - jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }} - ossrh-s01-staging-profile: ${{ secrets.OSSRH_S01_STAGING_PROFILE }} - ossrh-s01-token-password: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }} - ossrh-s01-token-username: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }} - spring-restdocs-version: ${{ needs.build-and-stage-release.outputs.version }} - promote-release: - name: Promote Release - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1 - env: - JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }} - - name: Promote Open Source Build - run: jfrog rt build-promote ${{ format('spring-restdocs-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local - create-github-release: - name: Create GitHub Release - needs: - - build-and-stage-release - - promote-release - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Create GitHub Release - uses: ./.github/actions/create-github-release - with: - milestone: ${{ needs.build-and-stage-release.outputs.version }} - token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c01583a59..000000000 --- a/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.classpath -.gradle -.project -.settings -bin -build -!buildSrc/src/main/groovy/org/springframework/restdocs/build/ -target -.idea/* -!.idea/icon.svg -*.iml \ No newline at end of file diff --git a/.idea/icon.svg b/.idea/icon.svg deleted file mode 100644 index 5e6592046..000000000 --- a/.idea/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.sdkmanrc b/.sdkmanrc deleted file mode 100644 index bea2d5156..000000000 --- a/.sdkmanrc +++ /dev/null @@ -1,3 +0,0 @@ -# Enable auto-env through the sdkman_auto_env config -# Add key=value pairs of SDKs to use below -java=17.0.15-librca diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index b8dfcb906..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributor Code of Conduct - -As contributors and maintainers of this project, and in the interest of fostering an open -and welcoming community, we pledge to respect all people who contribute through reporting -issues, posting feature requests, updating documentation, submitting pull requests or -patches, and other activities. - -We are committed to making participation in this project a harassment-free experience for -everyone, regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, body size, race, ethnicity, age, -religion, or nationality. - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments -* Public or private harassment -* Publishing other's private information, such as physical or electronic addresses, - without explicit permission -* Other unethical or unprofessional conduct - -Project maintainers have the right and responsibility to remove, edit, or reject comments, -commits, code, wiki edits, issues, and other contributions that are not aligned to this -Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors -that they deem inappropriate, threatening, offensive, or harmful. - -By adopting this Code of Conduct, project maintainers commit themselves to fairly and -consistently applying these principles to every aspect of managing this project. Project -maintainers who do not follow or enforce the Code of Conduct may be permanently removed -from the project team. - -This Code of Conduct applies both within project spaces and in public spaces when an -individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by -contacting a project maintainer at spring-code-of-conduct@pivotal.io. All complaints will -be reviewed and investigated and will result in a response that is deemed necessary and -appropriate to the circumstances. Maintainers are obligated to maintain confidentiality -with regard to the reporter of an incident. - -This Code of Conduct is adapted from the [Contributor Covenant][1], version 1.3.0, available -at [contributor-covenant.org/version/1/3/0/][2]. - -[1]: https://contributor-covenant.org -[2]: https://contributor-covenant.org/version/1/3/0/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 24e7b4254..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,59 +0,0 @@ -# Contributing to Spring REST Docs - -Spring REST Docs is released under the Apache 2.0 license. -If you would like to contribute something, or simply want to work with the code, this document should help you to get started. - -## Code of conduct - -This project adheres to the Contributor Covenant [code of conduct][1]. By participating, you are expected to uphold this code. -Please report unacceptable behavior to spring-code-of-conduct@pivotal.io. - -## Include a Signed-off-by Trailer - -All commits must include a _Signed-off-by_ trailer at the end of each commit message to indicate that the contributor agrees to the [Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin). -For additional details, please refer to the ["Hello DCO, Goodbye CLA: Simplifying Contributions to Spring"](https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring) blog post. - -## Code conventions and housekeeping - -None of these is essential for a pull request, but they will all help - -- Make sure all new `.java` files to have a simple Javadoc class comment with at least an `@author` tag identifying you, and preferably at least a paragraph on what the class is for -- Add the ASF license header comment to all new `.java` files (copy from existing files in the project) -- Add yourself as an `@author` to the .java files that you modify substantially (more than cosmetic changes) -- Add some Javadocs -- Add unit tests that covers and new or modified functionality -- Whenever possible, please rebase your branch against the current main (or other target branch in the project) -- When writing a commit message please follow [these conventions][3] - Also, if you are fixing an existing issue please add `Fixes gh-nnn` at the end of the commit message (where nnn is the issue number) - -## Working with the code - -### Building from source - -To build the source you will need Java 17 or later. -The code is built with Gradle: - -``` -$ ./gradlew build -``` - -To build the samples, run the following command: - -``` -$ ./gradlew buildSamples -``` - -### Importing into Eclipse - -The project has Gradle's Eclipse plugin applied. -Eclipse project and classpath metadata can be generated by running the `eclipse` task: - -``` -$ ./gradlew eclipse -``` - -The project can then be imported into Eclipse using `File -> Import…` and then selecting `General -> Existing Projects into Workspace`. - -[1]: CODE_OF_CONDUCT.md -[2]: https://cla.pivotal.io/sign/spring -[3]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 823c1c8e9..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index e3dec714d..000000000 --- a/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Spring REST Docs [![Build status][1]][2] [![Revved up by Develocity][23]][24] - -## Overview - -The primary goal of this project is to make it easy to document RESTful services by combining content that's been hand-written using [Asciidoctor][3] with auto-generated examples produced with the [Spring MVC Test][4] framework. -The result is intended to be an easy-to-read user guide, akin to [GitHub's API documentation][5] for example, rather than the fully automated, dense API documentation produced by tools like [Swagger][6]. - -For a broader introduction see the Documenting RESTful APIs presentation. -Both the [slides][7] and a [video recording][8] are available. - -## Learning more - -To learn more about Spring REST Docs, please consult the [reference documentation][9]. - -## Building from source - -You will need Java 17 or later to build Spring REST Docs. -It is built using [Gradle][10]: - -``` -./gradlew build -``` - -## Contributing - -Contributors to this project agree to uphold its [code of conduct][11]. - -There are several that you can contribute to Spring REST Docs: - - - Open a [pull request][12]. Please see the [contributor guidelines][13] for details - - Ask and answer questions on Stack Overflow using the [`spring-restdocs`][15] tag - -## Third-party extensions - -| Name | Description | -| ---- | ----------- | -| [restdocs-wiremock][16] | Auto-generate WireMock stubs as part of documenting your RESTful API | -| [restdocsext-jersey][17] | Enables Spring REST Docs to be used with [Jersey's test framework][18] | -| [spring-auto-restdocs][19] | Uses introspection and Javadoc to automatically document request and response parameters | -| [restdocs-api-spec][20] | A Spring REST Docs extension that adds API specification support. It currently supports [OpenAPI 2][21] and [OpenAPI 3][22] | - -## Licence - -Spring REST Docs is open source software released under the [Apache 2.0 license][14]. - -[1]: https://github.com/spring-projects/spring-restdocs/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=main (Build status) -[2]: https://github.com/spring-projects/spring-restdocs/actions/workflows/build-and-deploy-snapshot.yml -[3]: https://asciidoctor.org -[4]: https://docs.spring.io/spring-framework/docs/4.1.x/spring-framework-reference/htmlsingle/#spring-mvc-test-framework -[5]: https://developer.github.com/v3/ -[6]: https://swagger.io -[7]: https://speakerdeck.com/ankinson/documenting-restful-apis-webinar -[8]: https://www.youtube.com/watch?v=knH5ihPNiUs&feature=youtu.be -[9]: https://docs.spring.io/spring-restdocs/docs/ -[10]: https://gradle.org -[11]: CODE_OF_CONDUCT.md -[12]: https://help.github.com/articles/using-pull-requests/ -[13]: CONTRIBUTING.md -[14]: https://www.apache.org/licenses/LICENSE-2.0.html -[15]: https://stackoverflow.com/tags/spring-restdocs -[16]: https://github.com/ePages-de/restdocs-wiremock -[17]: https://github.com/RESTDocsEXT/restdocsext-jersey -[18]: https://jersey.java.net/documentation/latest/test-framework.html -[19]: https://github.com/ScaCap/spring-auto-restdocs -[20]: https://github.com/ePages-de/restdocs-api-spec -[21]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md -[22]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md -[23]: https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A -[24]: https://ge.spring.io/scans?search.rootProjectNames=spring-restdocs diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 32bea2f44..000000000 --- a/build.gradle +++ /dev/null @@ -1,174 +0,0 @@ -plugins { - id 'base' - id 'io.spring.javaformat' version "$javaFormatVersion" apply false - id 'io.spring.nohttp' version '0.0.11' apply false - id 'maven-publish' -} - -allprojects { - group = "org.springframework.restdocs" - repositories { - mavenCentral() - maven { - url = "https://repo.spring.io/milestone" - content { - includeGroup "io.micrometer" - includeGroup "io.projectreactor" - includeGroup "org.springframework" - } - } - if (version.endsWith('-SNAPSHOT')) { - maven { url = "https://repo.spring.io/snapshot" } - } - } -} - -apply plugin: "io.spring.nohttp" -apply from: "${rootProject.projectDir}/gradle/publish-maven.gradle" - -nohttp { - source.exclude "buildSrc/.gradle/**" - source.exclude "**/build/**" - source.exclude "**/target/**" -} - -ext { - javadocLinks = [ - "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/", - "https://docs.jboss.org/hibernate/validator/9.0/api/", - "https://jakarta.ee/specifications/bean-validation/3.1/apidocs/" - ] as String[] -} - -check { - dependsOn checkstyleNohttp -} - -subprojects { subproject -> - plugins.withType(JavaPlugin) { - subproject.apply plugin: "io.spring.javaformat" - subproject.apply plugin: "checkstyle" - - java { - sourceCompatibility = 17 - targetCompatibility = 17 - } - - configurations { - all { - resolutionStrategy.cacheChangingModulesFor 0, "minutes" - } - internal { - canBeConsumed = false - canBeResolved = false - } - compileClasspath.extendsFrom(internal) - runtimeClasspath.extendsFrom(internal) - testCompileClasspath.extendsFrom(internal) - testRuntimeClasspath.extendsFrom(internal) - } - - test { - testLogging { - exceptionFormat = "full" - } - } - - tasks.withType(JavaCompile) { - options.compilerArgs = [ "-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes", "-Xlint:varargs", "-Xlint:options" ] - options.encoding = "UTF-8" - } - - tasks.withType(Test) { - maxHeapSize = "1024M" - } - - checkstyle { - configFile = rootProject.file("config/checkstyle/checkstyle.xml") - configProperties = [ "checkstyle.config.dir" : rootProject.file("config/checkstyle") ] - toolVersion = "10.12.4" - } - - dependencies { - checkstyle("com.puppycrawl.tools:checkstyle:${checkstyle.toolVersion}") - checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}") - } - - plugins.withType(MavenPublishPlugin) { - javadoc { - description = "Generates project-level javadoc for use in -javadoc jar" - options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED - options.author = true - options.header = "Spring REST Docs $version" - options.docTitle = "${options.header} API" - options.links = javadocLinks - options.addStringOption "-quiet" - options.encoding = "UTF-8" - options.source = "17" - } - - java { - withJavadocJar() - withSourcesJar() - } - } - } - plugins.withType(MavenPublishPlugin) { - subproject.apply from: "${rootProject.projectDir}/gradle/publish-maven.gradle" - } - tasks.withType(GenerateModuleMetadata) { - enabled = false - } - -} - -task api (type: Javadoc) { - group = "Documentation" - description = "Generates aggregated Javadoc API documentation." - project.rootProject.gradle.projectsEvaluated { - Set excludedProjects = ['spring-restdocs-asciidoctor'] - Set publishedProjects = rootProject.subprojects.findAll { it != project} - .findAll { it.plugins.hasPlugin(JavaPlugin) && it.plugins.hasPlugin(MavenPublishPlugin) } - .findAll { !excludedProjects.contains(it.name) } - .findAll { !it.name.startsWith('spring-boot-starter') } - dependsOn publishedProjects.javadoc - source publishedProjects.javadoc.source - classpath = project.files(publishedProjects.javadoc.classpath) - destinationDir = project.file "${buildDir}/docs/javadoc" - options { - author = true - docTitle = "Spring REST Docs ${project.version} API" - encoding = "UTF-8" - memberLevel = "protected" - outputLevel = "quiet" - source = "17" - splitIndex = true - use = true - windowTitle = "Spring REST Docs ${project.version} API" - } - } -} - -task docsZip(type: Zip, dependsOn: [":docs:asciidoctor", ":api"]) { - group = "Distribution" - archiveBaseName = "spring-restdocs" - archiveClassifier = "docs" - description = "Builds -${archiveClassifier} archive containing API and reference documentation" - destinationDirectory = file("${project.buildDir}/distributions") - - from(project.tasks.findByPath(":docs:asciidoctor")) { - into "reference/htmlsingle" - } - - from(api) { - into "api" - } -} - -publishing { - publications { - maven(MavenPublication) { - artifact docsZip - } - } -} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index 6746f1805..000000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id "java-gradle-plugin" -} diff --git a/buildSrc/src/main/java/org/springframework/restdocs/build/optional/OptionalDependenciesPlugin.java b/buildSrc/src/main/java/org/springframework/restdocs/build/optional/OptionalDependenciesPlugin.java deleted file mode 100644 index cb8642785..000000000 --- a/buildSrc/src/main/java/org/springframework/restdocs/build/optional/OptionalDependenciesPlugin.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.build.optional; - -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.attributes.Usage; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.JavaPluginExtension; -import org.gradle.api.tasks.SourceSetContainer; -import org.gradle.api.tasks.javadoc.Javadoc; -import org.gradle.plugins.ide.eclipse.EclipsePlugin; -import org.gradle.plugins.ide.eclipse.model.EclipseModel; - -/** - * A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new - * {@code optional} configuration. The {@code optional} configuration is part of the - * project's compile and runtime classpath's but does not affect the classpath of - * dependent projects. - * - * @author Andy Wilkinson - */ -public class OptionalDependenciesPlugin implements Plugin { - - /** - * Name of the {@code optional} configuration. - */ - public static final String OPTIONAL_CONFIGURATION_NAME = "optional"; - - @Override - public void apply(Project project) { - Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME); - project.getConfigurations().all((configuration) -> { - if (configuration.getName().startsWith("testRuntimeClasspath_") || configuration.getName().startsWith("testCompileClasspath_")) { - configuration.extendsFrom(optional); - } - }); - optional.attributes((attributes) -> attributes.attribute(Usage.USAGE_ATTRIBUTE, - project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME))); - project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { - SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class) - .getSourceSets(); - sourceSets.all((sourceSet) -> { - sourceSet.setCompileClasspath(sourceSet.getCompileClasspath().plus(optional)); - sourceSet.setRuntimeClasspath(sourceSet.getRuntimeClasspath().plus(optional)); - }); - project.getTasks().withType(Javadoc.class) - .all((javadoc) -> javadoc.setClasspath(javadoc.getClasspath().plus(optional))); - }); - project.getPlugins().withType(EclipsePlugin.class, - (eclipsePlugin) -> project.getExtensions().getByType(EclipseModel.class) - .classpath((classpath) -> classpath.getPlusConfigurations().add(optional))); - } - -} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/optional-dependencies.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/optional-dependencies.properties deleted file mode 100644 index 3a981c6fb..000000000 --- a/buildSrc/src/main/resources/META-INF/gradle-plugins/optional-dependencies.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class:org.springframework.restdocs.build.optional.OptionalDependenciesPlugin diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml deleted file mode 100644 index 85f240e26..000000000 --- a/config/checkstyle/checkstyle-suppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml deleted file mode 100644 index 612ebd174..000000000 --- a/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/docs/build.gradle b/docs/build.gradle deleted file mode 100644 index fefe9a5fa..000000000 --- a/docs/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id "org.asciidoctor.jvm.convert" version "4.0.4" - id "java-library" -} - -configurations { - asciidoctorExt -} - -dependencies { - asciidoctorExt("io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.5") - - internal(platform(project(":spring-restdocs-platform"))) - internal(enforcedPlatform("org.springframework:spring-framework-bom:$springFrameworkVersion")) - - testImplementation(project(":spring-restdocs-mockmvc")) - testImplementation(project(":spring-restdocs-restassured")) - testImplementation(project(":spring-restdocs-webtestclient")) - testImplementation("jakarta.servlet:jakarta.servlet-api") - testImplementation("jakarta.validation:jakarta.validation-api") - testImplementation("org.testng:testng:6.9.10") - testImplementation("org.junit.jupiter:junit-jupiter-api") -} - -tasks.findByPath("artifactoryPublish")?.enabled = false - -tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { - baseDirFollowsSourceDir() -} - -jar { - enabled = false -} - -javadoc { - enabled = false -} - -asciidoctor { - configurations 'asciidoctorExt' - sources { - include "index.adoc" - } - attributes "revnumber": project.version, - "spring-Framework-version": springFrameworkVersion, - "branch-or-tag": project.version.endsWith("SNAPSHOT") ? "main": "v${project.version}" - inputs.files(sourceSets.test.java) - outputOptions { - backends "spring-html" - } -} diff --git a/docs/src/docs/asciidoc/configuration.adoc b/docs/src/docs/asciidoc/configuration.adoc deleted file mode 100644 index 24c23a47e..000000000 --- a/docs/src/docs/asciidoc/configuration.adoc +++ /dev/null @@ -1,198 +0,0 @@ -[[configuration]] -== Configuration - -This section covers how to configure Spring REST Docs. - - - -[[configuration-uris]] -=== Documented URIs - -This section covers configuring documented URIs. - - - -[[configuration-uris-mockmvc]] -==== MockMvc URI Customization - -When using MockMvc, the default configuration for URIs documented by Spring REST Docs is as follows: - -|=== -|Setting |Default - -|Scheme -|`http` - -|Host -|`localhost` - -|Port -|`8080` -|=== - -This configuration is applied by `MockMvcRestDocumentationConfigurer`. -You can use its API to change one or more of the defaults to suit your needs. -The following example shows how to do so: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/mockmvc/CustomUriConfiguration.java[tags=custom-uri-configuration] ----- - -NOTE: If the port is set to the default for the configured scheme (port 80 for HTTP or port 443 for HTTPS), it is omitted from any URIs in the generated snippets. - -TIP: To configure a request's context path, use the `contextPath` method on `MockHttpServletRequestBuilder`. - - - -[[configuration-uris-rest-assured]] -==== REST Assured URI Customization - -REST Assured tests a service by making actual HTTP requests. As a result, URIs must be -customized once the operation on the service has been performed but before it is -documented. A -<> is provided for this purpose. - - - -[[configuration-uris-webtestclient]] -==== WebTestClient URI Customization - -When using WebTestClient, the default base for URIs documented by Spring REST Docs is `http://localhost:8080`. -You can customize this base by using the {spring-framework-api}/org/springframework/test/web/reactive/server/WebTestClient.Builder.html#baseUrl-java.lang.String-[ `baseUrl(String)` method on `WebTestClient.Builder`]. -The following example shows how to do so: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/webtestclient/CustomUriConfiguration.java[tags=custom-uri-configuration] ----- -<1> Configure the base of documented URIs to be `https://api.example.com`. - - - -[[configuration-snippet-encoding]] -=== Snippet Encoding - -The default snippet encoding is `UTF-8`. -You can change the default snippet encoding by using the `RestDocumentationConfigurer` API. -For example, the following examples use `ISO-8859-1`: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/CustomEncoding.java[tags=custom-encoding] ----- - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/CustomEncoding.java[tags=custom-encoding] ----- - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/CustomEncoding.java[tags=custom-encoding] ----- - -TIP: When Spring REST Docs converts the content of a request or a response to a `String`, the `charset` specified in the `Content-Type` header is used if it is available. -In its absence, the JVM's default `Charset` is used. -You can configure the JVM's default `Charset` by using the `file.encoding` system property. - - - -[[configuration-snippet-template-format]] -=== Snippet Template Format - -The default snippet template format is Asciidoctor. -Markdown is also supported out of the box. -You can change the default format by using the `RestDocumentationConfigurer` API. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/CustomFormat.java[tags=custom-format] ----- - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/CustomFormat.java[tags=custom-format] ----- - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/CustomFormat.java[tags=custom-format] ----- - - - -[[configuration-default-snippets]] -=== Default Snippets - -Six snippets are produced by default: - -* `curl-request` -* `http-request` -* `http-response` -* `httpie-request` -* `request-body` -* `response-body` - -You can change the default snippet configuration during setup by using the `RestDocumentationConfigurer` API. -The following examples produce only the `curl-request` snippet by default: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/CustomDefaultSnippets.java[tags=custom-default-snippets] ----- - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/CustomDefaultSnippets.java[tags=custom-default-snippets] ----- - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/CustomDefaultSnippets.java[tags=custom-default-snippets] ----- - - - -[[configuration-default-preprocessors]] -=== Default Operation Preprocessors - -You can configure default request and response preprocessors during setup by using the `RestDocumentationConfigurer` API. -The following examples remove the `Foo` headers from all requests and pretty print all responses: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/CustomDefaultOperationPreprocessors.java[tags=custom-default-operation-preprocessors] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/CustomDefaultOperationPreprocessors.java[tags=custom-default-operation-preprocessors] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/CustomDefaultOperationPreprocessors.java[tags=custom-default-operation-preprocessors] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - - diff --git a/docs/src/docs/asciidoc/contributing.adoc b/docs/src/docs/asciidoc/contributing.adoc deleted file mode 100644 index 0093ed075..000000000 --- a/docs/src/docs/asciidoc/contributing.adoc +++ /dev/null @@ -1,33 +0,0 @@ -[[contributing]] -== Contributing - -Spring REST Docs is intended to make it easy for you to produce high-quality documentation for your RESTful services. -However, we cannot achieve that goal without your contributions. - - - -[[contributing-questions]] -=== Questions - -You can ask questions about Spring REST Docs on https://stackoverflow.com[Stack Overflow] by using the `spring-restdocs` tag. -Similarly, we encourage you to help your fellow Spring REST Docs users by answering questions. - - - -[[contributing-bugs]] -=== Bugs - -If you believe you have found a bug, please take a moment to search the {github}/issues?q=is%3Aissue[existing issues]. -If no one else has reported the problem, please {github}/issues/new[open a new issue] that describes the problem in detail and, ideally, includes a test that reproduces it. - - - -[[contributing-enhancements]] -=== Enhancements - -If you would like an enhancement to be made to Spring REST Docs, pull requests are most welcome. -The source code is on {github}[GitHub]. -You may want to search the {github}/issues?q=is%3Aissue[existing issues] and {github}/pulls?q=is%3Apr[pull requests] to see if the enhancement has already been proposed. -You may also want to {github}/issues/new[open a new issue] to discuss a possible enhancement before work on it begins. - - diff --git a/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc b/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc deleted file mode 100644 index b51512697..000000000 --- a/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc +++ /dev/null @@ -1,144 +0,0 @@ -[[customizing-requests-and-responses]] -== Customizing requests and responses - -There may be situations where you do not want to document a request exactly as it was sent or a response exactly as it was received. -Spring REST Docs provides a number of preprocessors that can be used to modify a request or response before it is documented. - -Preprocessing is configured by calling `document` with an `OperationRequestPreprocessor` or an `OperationResponsePreprocessor`. -You can obtain instances by using the static `preprocessRequest` and `preprocessResponse` methods on `Preprocessors`. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/PerTestPreprocessing.java[tags=preprocessing] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/PerTestPreprocessing.java[tags=preprocessing] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/PerTestPreprocessing.java[tags=preprocessing] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -Alternatively, you may want to apply the same preprocessors to every test. -You can do so by using the `RestDocumentationConfigurer` API in your `@Before` method to configure the preprocessors. -For example, to remove the `Foo` header from all requests and pretty print all responses, you could do one of the following (depending on your testing environment): - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/EveryTestPreprocessing.java[tags=setup] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/EveryTestPreprocessing.java[tags=setup] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/EveryTestPreprocessing.java[tags=setup] ----- -<1> Apply a request preprocessor that removes the header named `Foo`. -<2> Apply a response preprocessor that pretty prints its content. - -Then, in each test, you can perform any configuration specific to that test. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/EveryTestPreprocessing.java[tags=use] ----- - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/EveryTestPreprocessing.java[tags=use] ----- - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/EveryTestPreprocessing.java[tags=use] ----- - -Various built-in preprocessors, including those illustrated above, are available through the static methods on `Preprocessors`. -See <> for further details. - - - -[[customizing-requests-and-responses-preprocessors]] -=== Preprocessors - - - -[[customizing-requests-and-responses-preprocessors-pretty-print]] -==== Pretty Printing - -`prettyPrint` on `Preprocessors` formats the content of the request or response to make it easier to read. - - - -[[customizing-requests-and-responses-preprocessors-mask-links]] -==== Masking Links - -If you are documenting a hypermedia-based API, you may want to encourage clients to navigate the API by using links rather than through the use of hard coded URIs. -One way to do so is to limit the use of URIs in the documentation. -`maskLinks` on `Preprocessors` replaces the `href` of any links in the response with `...`. -You can also specify a different replacement if you wish. - - - -[[customizing-requests-and-responses-preprocessors-modify-headers]] -==== Modifying Headers - -You can use `modifyHeaders` on `Preprocessors` to add, set, and remove request or response headers. - - - -[[customizing-requests-and-responses-preprocessors-replace-patterns]] -==== Replacing Patterns - -`replacePattern` on `Preprocessors` provides a general purpose mechanism for replacing content in a request or response. -Any occurrences that match a regular expression are replaced. - - - -[[customizing-requests-and-responses-preprocessors-modify-uris]] -==== Modifying URIs - -TIP: If you use MockMvc or a WebTestClient that is not bound to a server, you should customize URIs by <>. - -You can use `modifyUris` on `Preprocessors` to modify any URIs in a request or a response. -When using REST Assured or WebTestClient bound to a server, this lets you customize the URIs that appear in the documentation while testing a local instance of the service. - - - -[[customizing-requests-and-responses-preprocessors-writing-your-own]] -==== Writing Your Own Preprocessor - -If one of the built-in preprocessors does not meet your needs, you can write your own by implementing the `OperationPreprocessor` interface. -You can then use your custom preprocessor in exactly the same way as any of the built-in preprocessors. - -If you want to modify only the content (body) of a request or response, consider implementing the `ContentModifier` interface and using it with the built-in `ContentModifyingOperationPreprocessor`. - - diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc deleted file mode 100644 index cb9ce82a3..000000000 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ /dev/null @@ -1,1398 +0,0 @@ -[[documenting-your-api]] -== Documenting your API - -This section provides more details about using Spring REST Docs to document your API. - - - -[[documenting-your-api-hypermedia]] -=== Hypermedia - -Spring REST Docs provides support for documenting the links in a https://en.wikipedia.org/wiki/HATEOAS[hypermedia-based] API. -The following examples show how to use it: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Hypermedia.java[tag=links] ----- -<1> Configure Spring REST docs to produce a snippet describing the response's links. -Uses the static `links` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -<2> Expect a link whose `rel` is `alpha`. -Uses the static `linkWithRel` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -<3> Expect a link whose `rel` is `bravo`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Hypermedia.java[tag=links] ----- -<1> Configure Spring REST docs to produce a snippet describing the response's links. -Uses the static `links` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -<2> Expect a link whose `rel` is `alpha`. -Uses the static `linkWithRel` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -<3> Expect a link whose `rel` is `bravo`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Hypermedia.java[tag=links] ----- -<1> Configure Spring REST docs to produce a snippet describing the response's links. - Uses the static `links` method on - `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -<2> Expect a link whose `rel` is `alpha`. Uses the static `linkWithRel` method on - `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -<3> Expect a link whose `rel` is `bravo`. - -The result is a snippet named `links.adoc` that contains a table describing the resource's links. - -TIP: If a link in the response has a `title`, you can omit the description from its descriptor and the `title` is used. -If you omit the description and the link does not have a `title`, a failure occurs. - -When documenting links, the test fails if an undocumented link is found in the response. -Similarly, the test also fails if a documented link is not found in the response and the link has not been marked as optional. - -If you do not want to document a link, you can mark it as ignored. -Doing so prevents it from appearing in the generated snippet while avoiding the failure described above. - -You can also document links in a relaxed mode, where any undocumented links do not cause a test failure. -To do so, use the `relaxedLinks` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the links. - - - -[[documenting-your-api-hypermedia-link-formats]] -==== Hypermedia Link Formats - -Two link formats are understood by default: - -* Atom: Links are expected to be in an array named `links`. - This is used by default when the content type of the response is compatible with `application/json`. -* HAL: Links are expected to be in a map named `_links`. - This is used by default when the content type of the response is compatible with `application/hal+json`. - -If you use Atom- or HAL-format links but with a different content type, you can provide one of the built-in `LinkExtractor` implementations to `links`. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Hypermedia.java[tag=explicit-extractor] ----- -<1> Indicate that the links are in HAL format. -Uses the static `halLinks` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Hypermedia.java[tag=explicit-extractor] ----- -<1> Indicate that the links are in HAL format. -Uses the static `halLinks` method on `org.springframework.restdocs.hypermedia.HypermediaDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Hypermedia.java[tag=explicit-extractor] ----- -<1> Indicate that the links are in HAL format. Uses the static `halLinks` method on -`org.springframework.restdocs.hypermedia.HypermediaDocumentation`. - -If your API represents its links in a format other than Atom or HAL, you can provide your own implementation of the `LinkExtractor` interface to extract the links from the response. - - - -[[documenting-your-api-hypermedia-ignoring-common-links]] -==== Ignoring Common Links - -Rather than documenting links that are common to every response, such as `self` and `curies` when using HAL, you may want to document them once in an overview section and then ignore them in the rest of your API's documentation. -To do so, you can build on the <> to add link descriptors to a snippet that is preconfigured to ignore certain links. -The following example shows how to do so: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/Hypermedia.java[tags=ignore-links] ----- - - - -[[documenting-your-api-request-response-payloads]] -=== Request and Response Payloads - -In addition to the hypermedia-specific support <>, support for general documentation of request and response payloads is also provided. - -By default, Spring REST Docs automatically generates snippets for the body of the request and the body of the response. -These snippets are named `request-body.adoc` and `response-body.adoc` respectively. - - - -[[documenting-your-api-request-response-payloads-fields]] -==== Request and Response Fields - -To provide more detailed documentation of a request or response payload, support for documenting the payload's fields is provided. - -Consider the following payload: - -[source,json,indent=0] ----- - { - "contact": { - "name": "Jane Doe", - "email": "jane.doe@example.com" - } - } ----- - -You can document the previous example's fields as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=response] ----- -<1> Configure Spring REST docs to produce a snippet describing the fields in the response payload. -To document a request, you can use `requestFields`. -Both are static methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -<2> Expect a field with the path `contact.email`. -Uses the static `fieldWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. -<3> Expect a field with the path `contact.name`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=response] ----- -<1> Configure Spring REST docs to produce a snippet describing the fields in the response payload. -To document a request, you can use `requestFields`. -Both are static methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -<2> Expect a field with the path `contact.email`. -Uses the static `fieldWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. -<3> Expect a field with the path `contact.name`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=response] ----- -<1> Configure Spring REST docs to produce a snippet describing the fields in the response payload. -To document a request, you can use `requestFields`. -Both are static methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -<2> Expect a field with the path `contact.email`. -Uses the static `fieldWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. -<3> Expect a field with the path `contact.name`. - -The result is a snippet that contains a table describing the fields. -For requests, this snippet is named `request-fields.adoc`. -For responses, this snippet is named `response-fields.adoc`. - -When documenting fields, the test fails if an undocumented field is found in the payload. -Similarly, the test also fails if a documented field is not found in the payload and the field has not been marked as optional. - -If you do not want to provide detailed documentation for all of the fields, an entire subsection of a payload can be documented. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=subsection] ----- -<1> Document the subsection with the path `contact`. -`contact.email` and `contact.name` are now seen as having also been documented. -Uses the static `subsectionWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=subsection] ----- -<1> Document the subsection with the path `contact`. -`contact.email` and `contact.name` are now seen as having also been documented. -Uses the static `subsectionWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=subsection] ----- -<1> Document the subsection with the path `contact`. `contact.email` and `contact.name` are now seen as having also been documented. -Uses the static `subsectionWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. - -`subsectionWithPath` can be useful for providing a high-level overview of a particular section of a payload. -You can then produce separate, more detailed documentation for a subsection. -See <>. - -If you do not want to document a field or subsection at all, you can mark it as ignored. -This prevents it from appearing in the generated snippet while avoiding the failure described earlier. - -You can also document fields in a relaxed mode, where any undocumented fields do not cause a test failure. -To do so, use the `relaxedRequestFields` and `relaxedResponseFields` methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -This can be useful when documenting a particular scenario where you want to focus only on a subset of the payload. - -TIP: By default, Spring REST Docs assumes that the payload you are documenting is JSON. -If you want to document an XML payload, the content type of the request or response must be compatible with `application/xml`. - - - -[[documenting-your-api-request-response-payloads-fields-json]] -===== Fields in JSON Payloads - -This section describes how to work with fields in JSON payloads. - - - -[[documenting-your-api-request-response-payloads-fields-json-field-paths]] -====== JSON Field Paths - -JSON field paths use either dot notation or bracket notation. -Dot notation uses '.' to separate each key in the path (for example, `a.b`). -Bracket notation wraps each key in square brackets and single quotation marks (for example, `['a']['b']`). -In either case, `[]` is used to identify an array. -Dot notation is more concise, but using bracket notation enables the use of `.` within a key name (for example, `['a.b']`). -The two different notations can be used in the same path (for example, `a['b']`). - -Consider the following JSON payload: - -[source,json,indent=0] ----- - { - "a":{ - "b":[ - { - "c":"one" - }, - { - "c":"two" - }, - { - "d":"three" - } - ], - "e.dot" : "four" - } - } ----- - -In the preceding JSON payload, the following paths are all present: - -[cols="1,3"] -|=== -|Path | Value - -|`a` -|An object containing `b` - -|`a.b` -|An array containing three objects - -|`['a']['b']` -|An array containing three objects - -|`a['b']` -|An array containing three objects - -|`['a'].b` -|An array containing three objects - -|`a.b[]` -|An array containing three objects - -|`a.b[].c` -|An array containing the strings `one` and `two` - -|`a.b[].d` -|The string `three` - -|`a['e.dot']` -|The string `four` - -|`['a']['e.dot']` -|The string `four` -|=== - -You can also document a payload that uses an array at its root. -The path `[]` refers to the entire array. -You can then use bracket or dot notation to identify fields within the array's entries. -For example, `[].id` corresponds to the `id` field of every object found in the following array: - -[source,json,indent=0] ----- - [ - { - "id":1 - }, - { - "id":2 - } - ] ----- - -You can use `\*` as a wildcard to match fields with different names. -For example, `users.*.role` could be used to document the role of every user in the following JSON: - -[source,json,indent=0] ----- - { - "users":{ - "ab12cd34":{ - "role": "Administrator" - }, - "12ab34cd":{ - "role": "Guest" - } - } - } ----- - - - -[[documenting-your-api-request-response-payloads-fields-json-field-types]] -====== JSON Field Types - -When a field is documented, Spring REST Docs tries to determine its type by examining the payload. -Seven different types are supported: - -[cols="1,3"] -|=== -| Type | Description - -| `array` -| The value of each occurrence of the field is an array. - -| `boolean` -| The value of each occurrence of the field is a boolean (`true` or `false`). - -| `object` -| The value of each occurrence of the field is an object. - -| `number` -| The value of each occurrence of the field is a number. - -| `null` -| The value of each occurrence of the field is `null`. - -| `string` -| The value of each occurrence of the field is a string. - -| `varies` -| The field occurs multiple times in the payload with a variety of different types. -|=== - -You can also explicitly set the type by using the `type(Object)` method on `FieldDescriptor`. -The result of the `toString` method of the supplied `Object` is used in the documentation. -Typically, one of the values enumerated by `JsonFieldType` is used. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=explicit-type] ----- -<1> Set the field's type to `String`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=explicit-type] ----- -<1> Set the field's type to `String`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=explicit-type] ----- -<1> Set the field's type to `String`. - - - -[[documenting-your-api-request-response-payloads-fields-xml]] -===== XML payloads - -This section describes how to work with XML payloads. - - - -[[documenting-your-api-request-response-payloads-fields-xml-field-paths]] -====== XML Field Paths - -XML field paths are described using XPath. -`/` is used to descend into a child node. - - - -[[documenting-your-api-request-response-payloads-fields-xml-field-types]] -====== XML Field Types - -When documenting an XML payload, you must provide a type for the field by using the `type(Object)` method on `FieldDescriptor`. -The result of the supplied type's `toString` method is used in the documentation. - - - -[[documenting-your-api-request-response-payloads-fields-reusing-field-descriptors]] -===== Reusing Field Descriptors - -In addition to the general support for <>, the request and response snippets let additional descriptors be configured with a path prefix. -This lets the descriptors for a repeated portion of a request or response payload be created once and then reused. - -Consider an endpoint that returns a book: - -[source,json,indent=0] ----- - { - "title": "Pride and Prejudice", - "author": "Jane Austen" - } ----- - -The paths for `title` and `author` are `title` and `author`, respectively. - -Now consider an endpoint that returns an array of books: - -[source,json,indent=0] ----- - [{ - "title": "Pride and Prejudice", - "author": "Jane Austen" - }, - { - "title": "To Kill a Mockingbird", - "author": "Harper Lee" - }] ----- - -The paths for `title` and `author` are `[].title` and `[].author`, respectively. -The only difference between the single book and the array of books is that the fields' paths now have a `[].` prefix. - -You can create the descriptors that document a book as follows: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/Payload.java[tags=book-descriptors] ----- - -You can then use them to document a single book, as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=single-book] ----- -<1> Document `title` and `author` by using existing descriptors - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=single-book] ----- -<1> Document `title` and `author` by using existing descriptors - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=single-book] ----- -<1> Document `title` and `author` by using existing descriptors - -You can also use the descriptors to document an array of books, as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=book-array] ----- -<1> Document the array. -<2> Document `[].title` and `[].author` by using the existing descriptors prefixed with `[].` - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=book-array] ----- -<1> Document the array. -<2> Document `[].title` and `[].author` by using the existing descriptors prefixed with `[].` - - - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=book-array] ----- -<1> Document the array. -<2> Document `[].title` and `[].author` by using the existing descriptors prefixed with `[].` - - - -[[documenting-your-api-request-response-payloads-subsections]] -==== Documenting a Subsection of a Request or Response Payload - -If a payload is large or structurally complex, it can be useful to document individual sections of the payload. -REST Docs let you do so by extracting a subsection of the payload and then documenting it. - - - -[[documenting-your-api-request-response-payloads-subsections-body]] -===== Documenting a Subsection of a Request or Response Body - -Consider the following JSON response body: - -[source,json,indent=0] ----- - { - "weather": { - "wind": { - "speed": 15.3, - "direction": 287.0 - }, - "temperature": { - "high": 21.2, - "low": 14.8 - } - } - } ----- - -You can produce a snippet that documents the `temperature` object as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=body-subsection] ----- -<1> Produce a snippet containing a subsection of the response body. -Uses the static `responseBody` and `beneathPath` methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -To produce a snippet for the request body, you can use `requestBody` in place of `responseBody`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=body-subsection] ----- -<1> Produce a snippet containing a subsection of the response body. -Uses the static `responseBody` and `beneathPath` methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -To produce a snippet for the request body, you can use `requestBody` in place of `responseBody`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=body-subsection] ----- -<1> Produce a snippet containing a subsection of the response body. -Uses the static `responseBody` and `beneathPath` methods on `org.springframework.restdocs.payload.PayloadDocumentation`. -To produce a snippet for the request body, you can use `requestBody` in place of `responseBody`. - -The result is a snippet with the following contents: - -[source,json,indent=0] ----- - { - "temperature": { - "high": 21.2, - "low": 14.8 - } - } ----- - -To make the snippet's name distinct, an identifier for the subsection is included. -By default, this identifier is `beneath-${path}`. -For example, the preceding code results in a snippet named `response-body-beneath-weather.temperature.adoc`. -You can customize the identifier by using the `withSubsectionId(String)` method, as follows: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/Payload.java[tags=custom-subsection-id] ----- - -The result is a snippet named `request-body-temp.adoc`. - - - -[[documenting-your-api-request-response-payloads-subsections-fields]] -===== Documenting the Fields of a Subsection of a Request or Response - -As well as documenting a subsection of a request or response body, you can also document the fields in a particular subsection. -You can produce a snippet that documents the fields of the `temperature` object (`high` and `low`) as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=fields-subsection] ----- -<1> Produce a snippet describing the fields in the subsection of the response payload beneath the path `weather.temperature`. -Uses the static `beneathPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. -<2> Document the `high` and `low` fields. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=fields-subsection] ----- -<1> Produce a snippet describing the fields in the subsection of the response payload beneath the path `weather.temperature`. -Uses the static `beneathPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. -<2> Document the `high` and `low` fields. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=fields-subsection] ----- -<1> Produce a snippet describing the fields in the subsection of the response payload - beneath the path `weather.temperature`. Uses the static `beneathPath` method on - `org.springframework.restdocs.payload.PayloadDocumentation`. -<2> Document the `high` and `low` fields. - -The result is a snippet that contains a table describing the `high` and `low` fields of `weather.temperature`. -To make the snippet's name distinct, an identifier for the subsection is included. -By default, this identifier is `beneath-${path}`. -For example, the preceding code results in a snippet named `response-fields-beneath-weather.temperature.adoc`. - - - -[[documenting-your-api-query-parameters]] -=== Query Parameters - -You can document a request's query parameters by using `queryParameters`. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/QueryParameters.java[tags=query-parameters] ----- -<1> Perform a `GET` request with two parameters, `page` and `per_page`, in the query string. -Query parameters should be included in the URL, as shown here, or specified using the request builder's `queryParam` or `queryParams` method. -The `param` and `params` methods should be avoided as the source of the parameters is then ambiguous. -<2> Configure Spring REST Docs to produce a snippet describing the request's query parameters. -Uses the static `queryParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the `page` query parameter. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<4> Document the `per_page` query parameter. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/QueryParameters.java[tags=query-parameters] ----- -<1> Perform a `GET` request with two parameters, `page` and `per_page`, in the query string. -<2> Configure Spring REST Docs to produce a snippet describing the request's query parameters. -Uses the static `queryParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the `page` parameter. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<4> Document the `per_page` parameter. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/QueryParameters.java[tags=query-parameters] ----- -<1> Configure Spring REST Docs to produce a snippet describing the request's query parameters. -Uses the static `queryParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<2> Document the `page` parameter. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the `per_page` parameter. -<4> Perform a `GET` request with two parameters, `page` and `per_page`, in the query string. - -When documenting query parameters, the test fails if an undocumented query parameter is used in the request's query string. -Similarly, the test also fails if a documented query parameter is not found in the request's query string and the parameter has not been marked as optional. - -If you do not want to document a query parameter, you can mark it as ignored. -This prevents it from appearing in the generated snippet while avoiding the failure described above. - -You can also document query parameters in a relaxed mode where any undocumented parameters do not cause a test failure. -To do so, use the `relaxedQueryParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the query parameters. - - - -[[documenting-your-api-form-parameters]] -=== Form Parameters - -You can document a request's form parameters by using `formParameters`. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/FormParameters.java[tags=form-parameters] ----- -<1> Perform a `POST` request with a single form parameter, `username`. -<2> Configure Spring REST Docs to produce a snippet describing the request's form parameters. -Uses the static `formParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the `username` parameter. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/FormParameters.java[tags=form-parameters] ----- -<1> Perform a `POST` request with a single form parameter, `username`. -<2> Configure Spring REST Docs to produce a snippet describing the request's form parameters. -Uses the static `formParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the `username` parameter. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/FormParameters.java[tags=form-parameters] ----- -<1> Configure Spring REST Docs to produce a snippet describing the request's form parameters. -Uses the static `formParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<2> Document the `username` parameter. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Perform a `POST` request with a single form parameter, `username`. - -In all cases, the result is a snippet named `form-parameters.adoc` that contains a table describing the form parameters that are supported by the resource. - -When documenting form parameters, the test fails if an undocumented form parameter is used in the request body. -Similarly, the test also fails if a documented form parameter is not found in the request body and the form parameter has not been marked as optional. - -If you do not want to document a form parameter, you can mark it as ignored. -This prevents it from appearing in the generated snippet while avoiding the failure described above. - -You can also document form parameters in a relaxed mode where any undocumented parameters do not cause a test failure. -To do so, use the `relaxedFormParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the form parameters. - - - -[[documenting-your-api-path-parameters]] -=== Path Parameters - -You can document a request's path parameters by using `pathParameters`. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/PathParameters.java[tags=path-parameters] ----- -<1> Perform a `GET` request with two path parameters, `latitude` and `longitude`. -<2> Configure Spring REST Docs to produce a snippet describing the request's path parameters. -Uses the static `pathParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the parameter named `latitude`. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<4> Document the parameter named `longitude`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/PathParameters.java[tags=path-parameters] ----- -<1> Perform a `GET` request with two path parameters, `latitude` and `longitude`. -<2> Configure Spring REST Docs to produce a snippet describing the request's path parameters. -Uses the static `pathParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the parameter named `latitude`. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<4> Document the parameter named `longitude`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/PathParameters.java[tags=path-parameters] ----- -<1> Configure Spring REST Docs to produce a snippet describing the request's path parameters. -Uses the static `pathParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -<2> Document the parameter named `latitude`. -Uses the static `parameterWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the parameter named `longitude`. -<4> Perform a `GET` request with two path parameters, `latitude` and `longitude`. - -The result is a snippet named `path-parameters.adoc` that contains a table describing the path parameters that are supported by the resource. - -When documenting path parameters, the test fails if an undocumented path parameter is used in the request. -Similarly, the test also fails if a documented path parameter is not found in the request and the path parameter has not been marked as optional. - -You can also document path parameters in a relaxed mode, where any undocumented parameters do not cause a test failure. -To do so, use the `relaxedPathParameters` method on `org.springframework.restdocs.request.RequestDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the path parameters. - -If you do not want to document a path parameter, you can mark it as ignored. -Doing so prevents it from appearing in the generated snippet while avoiding the failure described earlier. - - - -[[documenting-your-api-request-parts]] -=== Request Parts - -You can use `requestParts` to document the parts of a multipart request. -The following example shows how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/RequestParts.java[tags=request-parts] ----- -<1> Perform a `POST` request with a single part named `file`. -<2> Configure Spring REST Docs to produce a snippet describing the request's parts. -Uses the static `requestParts` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the part named `file`. -Uses the static `partWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/RequestParts.java[tags=request-parts] ----- -<1> Perform a `POST` request with a single part named `file`. -<2> Configure Spring REST Docs to produce a snippet describing the request's parts. -Uses the static `requestParts` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Document the part named `file`. -Uses the static `partWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/RequestParts.java[tags=request-parts] ----- -<1> Configure Spring REST Docs to produce a snippet describing the request's parts. -Uses the static `requestParts` method on `org.springframework.restdocs.request.RequestDocumentation`. -<2> Document the part named `file`. Uses the static `partWithName` method on `org.springframework.restdocs.request.RequestDocumentation`. -<3> Configure the request with the part named `file`. -<4> Perform the `POST` request to `/upload`. - -The result is a snippet named `request-parts.adoc` that contains a table describing the request parts that are supported by the resource. - -When documenting request parts, the test fails if an undocumented part is used in the request. -Similarly, the test also fails if a documented part is not found in the request and the part has not been marked as optional. - -You can also document request parts in a relaxed mode where any undocumented parts do not cause a test failure. -To do so, use the `relaxedRequestParts` method on `org.springframework.restdocs.request.RequestDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the request parts. - -If you do not want to document a request part, you can mark it as ignored. -This prevents it from appearing in the generated snippet while avoiding the failure described earlier. - - - -[[documenting-your-api-request-parts-payloads]] -=== Request Part Payloads - -You can document the payload of a request part in much the same way as the <>, with support for documenting a request part's body and its fields. - - - -[[documenting-your-api-request-parts-payloads-body]] -==== Documenting a Request Part's Body - -You can generate a snippet containing the body of a request part as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=body] ----- -<1> Configure Spring REST docs to produce a snippet containing the body of the request part named `metadata`. -Uses the static `requestPartBody` method on `PayloadDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/RequestPartPayload.java[tags=body] ----- -<1> Configure Spring REST docs to produce a snippet containing the body of the request part named `metadata`. -Uses the static `requestPartBody` method on `PayloadDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/RequestPartPayload.java[tags=body] ----- -<1> Configure Spring REST docs to produce a snippet containing the body of the request part named `metadata`. -Uses the static `requestPartBody` method on `PayloadDocumentation`. - -The result is a snippet named `request-part-${part-name}-body.adoc` that contains the part's body. -For example, documenting a part named `metadata` produces a snippet named `request-part-metadata-body.adoc`. - - - -[[documenting-your-api-request-parts-payloads-fields]] -==== Documenting a Request Part's Fields - -You can document a request part's fields in much the same way as the fields of a request or response, as follows: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=fields] ----- -<1> Configure Spring REST docs to produce a snippet describing the fields in the payload of the request part named `metadata`. -Uses the static `requestPartFields` method on `PayloadDocumentation`. -<2> Expect a field with the path `version`. -Uses the static `fieldWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/RequestPartPayload.java[tags=fields] ----- -<1> Configure Spring REST docs to produce a snippet describing the fields in the payload of the request part named `metadata`. -Uses the static `requestPartFields` method on `PayloadDocumentation`. -<2> Expect a field with the path `version`. -Uses the static `fieldWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/RequestPartPayload.java[tags=fields] ----- -<1> Configure Spring REST docs to produce a snippet describing the fields in the payload of the request part named `metadata`. -Uses the static `requestPartFields` method on `PayloadDocumentation`. -<2> Expect a field with the path `version`. -Uses the static `fieldWithPath` method on `org.springframework.restdocs.payload.PayloadDocumentation`. - -The result is a snippet that contains a table describing the part's fields. -This snippet is named `request-part-${part-name}-fields.adoc`. -For example, documenting a part named `metadata` produces a snippet named `request-part-metadata-fields.adoc`. - -When documenting fields, the test fails if an undocumented field is found in the payload of the part. -Similarly, the test also fails if a documented field is not found in the payload of the part and the field has not been marked as optional. -For payloads with a hierarchical structure, documenting a field is sufficient for all of its descendants to also be treated as having been documented. - -If you do not want to document a field, you can mark it as ignored. -Doing so prevents it from appearing in the generated snippet while avoiding the failure described above. - -You can also document fields in a relaxed mode, where any undocumented fields do not cause a test failure. -To do so, use the `relaxedRequestPartFields` method on `org.springframework.restdocs.payload.PayloadDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the payload of the part. - -For further information on describing fields, documenting payloads that use XML, and more, see the <>. - - - -[[documenting-your-api-http-headers]] -=== HTTP Headers - -You can document the headers in a request or response by using `requestHeaders` and `responseHeaders`, respectively. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/HttpHeaders.java[tags=headers] ----- -<1> Perform a `GET` request with an `Authorization` header that uses basic authentication. -<2> Configure Spring REST Docs to produce a snippet describing the request's headers. -Uses the static `requestHeaders` method on `org.springframework.restdocs.headers.HeaderDocumentation`. -<3> Document the `Authorization` header. -Uses the static `headerWithName` method on `org.springframework.restdocs.headers.HeaderDocumentation`. -<4> Produce a snippet describing the response's headers. -Uses the static `responseHeaders` method on `org.springframework.restdocs.headers.HeaderDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/HttpHeaders.java[tags=headers] ----- -<1> Perform a `GET` request with an `Authorization` header that uses basic authentication. -<2> Configure Spring REST Docs to produce a snippet describing the request's headers. -Uses the static `requestHeaders` method on `org.springframework.restdocs.headers.HeaderDocumentation`. -<3> Document the `Authorization` header. -Uses the static `headerWithName` method on `org.springframework.restdocs.headers.HeaderDocumentation`. -<4> Produce a snippet describing the response's headers. -Uses the static `responseHeaders` method on `org.springframework.restdocs.headers.HeaderDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/HttpHeaders.java[tags=headers] ----- -<1> Configure Spring REST Docs to produce a snippet describing the request's headers. -Uses the static `requestHeaders` method on `org.springframework.restdocs.headers.HeaderDocumentation`. -<2> Document the `Authorization` header. -Uses the static `headerWithName` method on `org.springframework.restdocs.headers.HeaderDocumentation. -<3> Produce a snippet describing the response's headers. -Uses the static `responseHeaders` method on `org.springframework.restdocs.headers.HeaderDocumentation`. -<4> Configure the request with an `Authorization` header that uses basic authentication. - -The result is a snippet named `request-headers.adoc` and a snippet named `response-headers.adoc`. -Each contains a table describing the headers. - -When documenting HTTP Headers, the test fails if a documented header is not found in the request or response. - - - -[[documenting-your-api-http-cookies]] -=== HTTP Cookies - -You can document the cookies in a request or response by using `requestCookies` and `responseCookies`, respectively. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/HttpCookies.java[tags=cookies] ----- -<1> Make a GET request with a `JSESSIONID` cookie. -<2> Configure Spring REST Docs to produce a snippet describing the request's cookies. - Uses the static `requestCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`. -<3> Document the `JSESSIONID` cookie. Uses the static `cookieWithName` method on `org.springframework.restdocs.cookies.CookieDocumentation`. -<4> Produce a snippet describing the response's cookies. - Uses the static `responseCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/HttpCookies.java[tags=cookies] ----- -<1> Make a GET request with a `JSESSIONID` cookie. -<2> Configure Spring REST Docs to produce a snippet describing the request's cookies. - Uses the static `requestCookies` method on - `org.springframework.restdocs.cookies.CookieDocumentation`. -<3> Document the `JSESSIONID` cookie. - Uses the static `cookieWithName` method on `org.springframework.restdocs.cookies.CookieDocumentation`. -<4> Produce a snippet describing the response's cookies. - Uses the static `responseCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/HttpCookies.java[tags=cookies] ----- -<1> Configure Spring REST Docs to produce a snippet describing the request's cookies. - Uses the static `requestCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`. -<2> Document the `JSESSIONID` cookie. - Uses the static `cookieWithName` method on `org.springframework.restdocs.cookies.CookieDocumentation`. -<3> Produce a snippet describing the response's cookies. - Uses the static `responseCookies` method on `org.springframework.restdocs.cookies.CookieDocumentation`. -<4> Send a `JSESSIONID` cookie with the request. - -The result is a snippet named `request-cookies.adoc` and a snippet named `response-cookies.adoc`. -Each contains a table describing the cookies. - -When documenting HTTP cookies, the test fails if an undocumented cookie is found in the request or response. -Similarly, the test also fails if a documented cookie is not found and the cookie has not been marked as optional. -You can also document cookies in a relaxed mode, where any undocumented cookies do not cause a test failure. -To do so, use the `relaxedRequestCookies` and `relaxedResponseCookies` methods on `org.springframework.restdocs.cookies.CookieDocumentation`. -This can be useful when documenting a particular scenario where you only want to focus on a subset of the cookies. -If you do not want to document a cookie, you can mark it as ignored. -Doing so prevents it from appearing in the generated snippet while avoiding the failure described earlier. - - - -[[documenting-your-api-reusing-snippets]] -=== Reusing Snippets - -It is common for an API that is being documented to have some features that are common across several of its resources. -To avoid repetition when documenting such resources, you can reuse a `Snippet` configured with the common elements. - -First, create the `Snippet` that describes the common elements. -The following example shows how to do so: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/SnippetReuse.java[tags=field] ----- - -Second, use this snippet and add further descriptors that are resource-specific. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/MockMvcSnippetReuse.java[tags=use] ----- -<1> Reuse the `pagingLinks` `Snippet`, calling `and` to add descriptors that are specific to the resource that is being documented. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/WebTestClientSnippetReuse.java[tags=use] ----- -<1> Reuse the `pagingLinks` `Snippet`, calling `and` to add descriptors that are specific to the resource that is being documented. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/RestAssuredSnippetReuse.java[tags=use] ----- -<1> Reuse the `pagingLinks` `Snippet`, calling `and` to add descriptors that are specific to the resource that is being documented. - -The result of the example is that links with `rel` values of `first`, `last`, `next`, `previous`, `alpha`, and `bravo` are all documented. - - - -[[documenting-your-api-constraints]] -=== Documenting Constraints - -Spring REST Docs provides a number of classes that can help you to document constraints. -You can use an instance of `ConstraintDescriptions` to access descriptions of a class's constraints. -The following example shows how to do so: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/Constraints.java[tags=constraints] ----- -<1> Create an instance of `ConstraintDescriptions` for the `UserInput` class. -<2> Get the descriptions of the `name` property's constraints. -This list contains two descriptions: one for the `NotNull` constraint and one for the `Size` constraint. - -The {samples}/restful-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java[`ApiDocumentation`] class in the Spring HATEOAS sample shows this functionality in action. - - - -[[documenting-your-api-constraints-finding]] -==== Finding Constraints - -By default, constraints are found by using a Bean Validation `Validator`. -Currently, only property constraints are supported. -You can customize the `Validator` that is used by creating `ConstraintDescriptions` with a custom `ValidatorConstraintResolver` instance. -To take complete control of constraint resolution, you can use your own implementation of `ConstraintResolver`. - - - -[[documenting-your-api-constraints-describing]] -==== Describing Constraints - -Default descriptions are provided for all of Bean Validation 3.1's constraints: - -* `AssertFalse` -* `AssertTrue` -* `DecimalMax` -* `DecimalMin` -* `Digits` -* `Email` -* `Future` -* `FutureOrPresent` -* `Max` -* `Min` -* `Negative` -* `NegativeOrZero` -* `NotBlank` -* `NotEmpty` -* `NotNull` -* `Null` -* `Past` -* `PastOrPresent` -* `Pattern` -* `Positive` -* `PositiveOrZero` -* `Size` - -Default descriptions are also provided for the following constraints from Hibernate -Validator: - -* `CodePointLength` -* `CreditCardNumber` -* `Currency` -* `EAN` -* `Email` -* `Length` -* `LuhnCheck` -* `Mod10Check` -* `Mod11Check` -* `NotBlank` -* `NotEmpty` -* `Currency` -* `Range` -* `SafeHtml` -* `URL` - -To override the default descriptions or to provide a new description, you can create a resource bundle with a base name of `org.springframework.restdocs.constraints.ConstraintDescriptions`. -The Spring HATEOAS-based sample contains {samples}/restful-notes-spring-hateoas/src/test/resources/org/springframework/restdocs/constraints/ConstraintDescriptions.properties[an example of such a resource bundle]. - -Each key in the resource bundle is the fully-qualified name of a constraint plus a `.description`. -For example, the key for the standard `@NotNull` constraint is `jakarta.validation.constraints.NotNull.description`. - -You can use a property placeholder referring to a constraint's attributes in its description. -For example, the default description of the `@Min` constraint, `Must be at least ${value}`, refers to the constraint's `value` attribute. - -To take more control of constraint description resolution, you can create `ConstraintDescriptions` with a custom `ResourceBundleConstraintDescriptionResolver`. -To take complete control, you can create `ConstraintDescriptions` with a custom `ConstraintDescriptionResolver` implementation. - - - -==== Using Constraint Descriptions in Generated Snippets - -Once you have a constraint's descriptions, you are free to use them however you like in the generated snippets. -For example, you may want to include the constraint descriptions as part of a field's description. -Alternatively, you could include the constraints as <> in the request fields snippet. -The {samples}/restful-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java[`ApiDocumentation`] class in the Spring HATEOAS-based sample illustrates the latter approach. - - - -[[documenting-your-api-default-snippets]] -=== Default Snippets - -A number of snippets are produced automatically when you document a request and response. - -[cols="1,3"] -|=== -|Snippet | Description - -| `curl-request.adoc` -| Contains the https://curl.haxx.se[`curl`] command that is equivalent to the `MockMvc` -call that is being documented. - -| `httpie-request.adoc` -| Contains the https://httpie.org[`HTTPie`] command that is equivalent to the `MockMvc` -call that is being documented. - -| `http-request.adoc` -| Contains the HTTP request that is equivalent to the `MockMvc` call that is being documented. - -| `http-response.adoc` -| Contains the HTTP response that was returned. - -| `request-body.adoc` -| Contains the body of the request that was sent. - -| `response-body.adoc` -| Contains the body of the response that was returned. -|=== - -You can configure which snippets are produced by default. -See the <> for more information. - - - -[[documentating-your-api-parameterized-output-directories]] -=== Using Parameterized Output Directories - -You can parameterize the output directory used by `document`. -The following parameters are supported: - -[cols="1,3"] -|=== -| Parameter | Description - -| {methodName} -| The unmodified name of the test method. - -| {method-name} -| The name of the test method, formatted using kebab-case. - -| {method_name} -| The name of the test method, formatted using snake_case. - -| {ClassName} -| The unmodified simple name of the test class. - -| {class-name} -| The simple name of the test class, formatted using kebab-case. - -| {class_name} -| The simple name of the test class, formatted using snake_case. - -| {step} -| The count of calls made to the service in the current test. -|=== - -For example, `document("{class-name}/{method-name}")` in a test method named `creatingANote` on the test class `GettingStartedDocumentation` writes snippets into a directory named `getting-started-documentation/creating-a-note`. - -A parameterized output directory is particularly useful in combination with a `@Before` method. -It lets documentation be configured once in a setup method and then reused in every test in the class. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/ParameterizedOutput.java[tags=parameterized-output] ----- - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/ParameterizedOutput.java[tags=parameterized-output] ----- - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/ParameterizedOutput.java[tags=parameterized-output] ----- - -With this configuration in place, every call to the service you are testing produces the <> without any further configuration. -Take a look at the `GettingStartedDocumentation` classes in each of the sample applications to see this functionality in action. - - - -[[documenting-your-api-customizing]] -=== Customizing the Output - -This section describes how to customize the output of Spring REST Docs. - - - -[[documenting-your-api-customizing-snippets]] -==== Customizing the Generated Snippets - -Spring REST Docs uses https://mustache.github.io[Mustache] templates to produce the generated snippets. -{source}/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates[Default templates] are provided for each of the snippets that Spring REST Docs can produce. -To customize a snippet's content, you can provide your own template. - -Templates are loaded from the classpath from an `org.springframework.restdocs.templates` subpackage. -The name of the subpackage is determined by the ID of the template format that is in use. -The default template format, Asciidoctor, has an ID of `asciidoctor`, so snippets are loaded from `org.springframework.restdocs.templates.asciidoctor`. -Each template is named after the snippet that it produces. -For example, to override the template for the `curl-request.adoc` snippet, create a template named `curl-request.snippet` in `src/test/resources/org/springframework/restdocs/templates/asciidoctor`. - - - -[[documenting-your-api-customizing-including-extra-information]] -==== Including Extra Information - -There are two ways to provide extra information for inclusion in a generated snippet: - -* Use the `attributes` method on a descriptor to add one or more attributes to it. -* Pass in some attributes when calling `curlRequest`, `httpRequest`, `httpResponse`, and so on. - Such attributes are associated with the snippet as a whole. - -Any additional attributes are made available during the template rendering process. -Coupled with a custom snippet template, this makes it possible to include extra information in a generated snippet. - -A concrete example is the addition of a constraints column and a title when documenting request fields. -The first step is to provide a `constraints` attribute for each field that you document and to provide a `title` attribute. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/Payload.java[tags=constraints] ----- -<1> Configure the `title` attribute for the request fields snippet. -<2> Set the `constraints` attribute for the `name` field. -<3> Set the `constraints` attribute for the `email` field. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/Payload.java[tags=constraints] ----- -<1> Configure the `title` attribute for the request fields snippet. -<2> Set the `constraints` attribute for the `name` field. -<3> Set the `constraints` attribute for the `email` field. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/Payload.java[tags=constraints] ----- -<1> Configure the `title` attribute for the request fields snippet. -<2> Set the `constraints` attribute for the `name` field. -<3> Set the `constraints` attribute for the `email` field. - -The second step is to provide a custom template named `request-fields.snippet` that includes the information about the fields' constraints in the generated snippet's table and adds a title. - -[source,indent=0] ----- - .{{title}} <1> - |=== - |Path|Type|Description|Constraints <2> - - {{#fields}} - |{{path}} - |{{type}} - |{{description}} - |{{constraints}} <3> - - {{/fields}} - |=== ----- -<1> Add a title to the table. -<2> Add a new column named "Constraints". -<3> Include the descriptors' `constraints` attribute in each row of the table. - - diff --git a/docs/src/docs/asciidoc/getting-started.adoc b/docs/src/docs/asciidoc/getting-started.adoc deleted file mode 100644 index 11be6100d..000000000 --- a/docs/src/docs/asciidoc/getting-started.adoc +++ /dev/null @@ -1,420 +0,0 @@ -[[getting-started]] -== Getting started - -This section describes how to get started with Spring REST Docs. - - - -[[getting-started-sample-applications]] -=== Sample Applications - -If you want to jump straight in, a number of https://github.com/spring-projects/spring-restdocs-samples[sample applications are available]. - - - -[[getting-started-requirements]] -=== Requirements - -Spring REST Docs has the following minimum requirements: - -* Java 17 -* Spring Framework 7 - -Additionally, the `spring-restdocs-restassured` module requires REST Assured 5.2. - - - -[[getting-started-build-configuration]] -=== Build configuration - -The first step in using Spring REST Docs is to configure your project's build. -The {samples}/restful-notes-spring-hateoas[Spring HATEOAS] and {samples}/restful-notes-spring-data-rest[Spring Data REST] samples contain a `build.gradle` and `pom.xml`, respectively, that you may wish to use as a reference. -The key parts of the configuration are described in the following listings: - -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - <1> - org.springframework.restdocs - spring-restdocs-mockmvc - {project-version} - test - - - - - <2> - org.asciidoctor - asciidoctor-maven-plugin - 2.2.1 - - - generate-docs - prepare-package <3> - - process-asciidoc - - - html - book - - - - - <4> - org.springframework.restdocs - spring-restdocs-asciidoctor - {project-version} - - - - - ----- -<1> Add a dependency on `spring-restdocs-mockmvc` in the `test` scope. -If you want to use `WebTestClient` or REST Assured rather than MockMvc, add a dependency on `spring-restdocs-webtestclient` or `spring-restdocs-restassured` respectively instead. -<2> Add the Asciidoctor plugin. -<3> Using `prepare-package` allows the documentation to be <>. -<4> Add `spring-restdocs-asciidoctor` as a dependency of the Asciidoctor plugin. -This will automatically configure the `snippets` attribute for use in your `.adoc` files to point to `target/generated-snippets`. -It will also allow you to use the `operation` block macro. -It requires AsciidoctorJ 3.0. - -[source,indent=0,subs="verbatim,attributes",role="secondary"] -.Gradle ----- - plugins { <1> - id "org.asciidoctor.jvm.convert" version "3.3.2" - } - - configurations { - asciidoctorExt <2> - } - - dependencies { - asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:{project-version}' <3> - testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:{project-version}' <4> - } - - ext { <5> - snippetsDir = file('build/generated-snippets') - } - - test { <6> - outputs.dir snippetsDir - } - - asciidoctor { <7> - inputs.dir snippetsDir <8> - configurations 'asciidoctorExt' <9> - dependsOn test <10> - } ----- -<1> Apply the Asciidoctor plugin. -<2> Declare the `asciidoctorExt` configuration for dependencies that extend Asciidoctor. -<3> Add a dependency on `spring-restdocs-asciidoctor` in the `asciidoctorExt` configuration. -This will automatically configure the `snippets` attribute for use in your `.adoc` files to point to `build/generated-snippets`. -It will also allow you to use the `operation` block macro. -It requires AsciidoctorJ 3.0. -<4> Add a dependency on `spring-restdocs-mockmvc` in the `testImplementation` configuration. -If you want to use `WebTestClient` or REST Assured rather than MockMvc, add a dependency on `spring-restdocs-webtestclient` or `spring-restdocs-restassured` respectively instead. -<5> Configure a `snippetsDir` property that defines the output location for generated snippets. -<6> Make Gradle aware that running the `test` task will write output to the snippetsDir. This is required for https://docs.gradle.org/current/userguide/incremental_build.html[incremental builds]. -<7> Configure the `asciidoctor` task. -<8> Make Gradle aware that running the task will read input from the snippetsDir. This is required for https://docs.gradle.org/current/userguide/incremental_build.html[incremental builds]. -<9> Configure the use of the `asciidoctorExt` configuration for extensions. -<10> Make the task depend on the `test` task so that the tests are run before the documentation is created. - - - -[[getting-started-build-configuration-packaging-the-documentation]] -==== Packaging the Documentation - -You may want to package the generated documentation in your project's jar file -- for example, to have it {spring-boot-docs}/#boot-features-spring-mvc-static-content[served as static content] by Spring Boot. -To do so, configure your project's build so that: - -1. The documentation is generated before the jar is built -2. The generated documentation is included in the jar - -The following listings show how to do so in both Maven and Gradle: - -[source,xml,indent=0,role="primary"] -.Maven ----- - <1> - org.asciidoctor - asciidoctor-maven-plugin - - - <2> - maven-resources-plugin - - - copy-resources - prepare-package - - copy-resources - - <3> - - ${project.build.outputDirectory}/static/docs - - - - - ${project.build.directory}/generated-docs - - - - - - - ----- -<1> The existing declaration for the Asciidoctor plugin. -<2> The resource plugin must be declared after the Asciidoctor plugin as they are bound to the same phase (`prepare-package`) and the resource plugin must run after the Asciidoctor plugin to ensure that the documentation is generated before it's copied. -If you are not using Spring Boot and its plugin management, declare the plugin with an appropriate ``. -<3> Copy the generated documentation into the build output's `static/docs` directory, from where it will be included in the jar file. - -[source,indent=0,role="secondary"] -.Gradle ----- - bootJar { - dependsOn asciidoctor <1> - from ("${asciidoctor.outputDir}") { <2> - into 'static/docs' - } - } ----- -<1> Ensure that the documentation has been generated before the jar is built. -<2> Copy the generated documentation into the jar's `static/docs` directory. - - - -[[getting-started-documentation-snippets]] -=== Generating Documentation Snippets - -Spring REST Docs uses Spring MVC's {spring-framework-docs}/testing.html#spring-mvc-test-framework[test framework], Spring WebFlux's {spring-framework-docs}/testing.html#webtestclient[`WebTestClient`], or https://rest-assured.io/[REST Assured] to make requests to the service that you are documenting. -It then produces documentation snippets for the request and the resulting response. - - - -[[getting-started-documentation-snippets-setup]] -==== Setting up Your Tests - -Exactly how you set up your tests depends on the test framework that you use. -Spring REST Docs provides first-class support for JUnit 5. -Other frameworks, such as TestNG, are also supported, although slightly more setup is required. - - - -[[getting-started-documentation-snippets-setup-junit-5]] -===== Setting up Your JUnit 5 Tests - -When using JUnit 5, the first step in generating documentation snippets is to apply the `RestDocumentationExtension` to your test class. -The following example shows how to do so: - -[source,java,indent=0] ----- -@ExtendWith(RestDocumentationExtension.class) -public class JUnit5ExampleTests { ----- - -When testing a typical Spring application, you should also apply the `SpringExtension`: - -[source,java,indent=0] ----- -@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) -public class JUnit5ExampleTests { ----- - -The `RestDocumentationExtension` is automatically configured with an output directory based on your project's build tool: - -[cols="2,5"] -|=== -| Build tool | Output directory - -| Maven -| `target/generated-snippets` - -| Gradle -| `build/generated-snippets` -|=== - -If you are using JUnit 5.1, you can override the default by registering the extension as a field in your test class and providing an output directory when creating it. -The following example shows how to do so: - -[source,java,indent=0] ----- -public class JUnit5ExampleTests { - - @RegisterExtension - final RestDocumentationExtension restDocumentation = new RestDocumentationExtension ("custom"); - -} ----- - -Next, you must provide a `@BeforeEach` method to configure MockMvc or WebTestClient, or REST Assured. -The following listings show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/ExampleApplicationTests.java[tags=setup] ----- -<1> The `MockMvc` instance is configured by using a `MockMvcRestDocumentationConfigurer`. -You can obtain an instance of this class from the static `documentationConfiguration()` method on `org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/ExampleApplicationTests.java[tags=setup] ----- -<1> The `WebTestClient` instance is configured by adding a `WebTestClientRestDocumentationConfigurer` as an `ExchangeFilterFunction`. -You can obtain an instance of this class from the static `documentationConfiguration()` method on `org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/ExampleApplicationTests.java[tags=setup] ----- -<1> REST Assured is configured by adding a `RestAssuredRestDocumentationConfigurer` as a `Filter`. -You can obtain an instance of this class from the static `documentationConfiguration()` method on `RestAssuredRestDocumentation` in the `org.springframework.restdocs.restassured` package. - -The configurer applies sensible defaults and also provides an API for customizing the configuration. -See the <> for more information. - - - - -[[getting-started-documentation-snippets-setup-manual]] -===== Setting up your tests without JUnit - -The configuration when JUnit is not being used is a little more involved as the test class must perform some lifecycle management. -The {samples}/testng[TestNG sample] illustrates the approach. - -First, you need a `ManualRestDocumentation` field. -The following example shows how to define it: - -[source,java,indent=0] ----- -private ManualRestDocumentation restDocumentation = new ManualRestDocumentation(); ----- - -Secondly, you must call `ManualRestDocumentation.beforeTest(Class, String)` before each test. -You can do so as part of the method that configures MockMvc, WebTestClient, or REST Assured. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/ExampleApplicationTestNgTests.java[tags=setup] ----- - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/ExampleApplicationTestNgTests.java[tags=setup] ----- - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/ExampleApplicationTestNgTests.java[tags=setup] ----- - -Finally, you must call `ManualRestDocumentation.afterTest` after each test. -The following example shows how to do so with TestNG: - -[source,java,indent=0] ----- -include::{examples-dir}/com/example/mockmvc/ExampleApplicationTestNgTests.java[tags=teardown] ----- - - - -[[getting-started-documentation-snippets-invoking-the-service]] -==== Invoking the RESTful Service - -Now that you have configured the testing framework, you can use it to invoke the RESTful service and document the request and response. -The following examples show how to do so: - -[source,java,indent=0,role="primary"] -.MockMvc ----- -include::{examples-dir}/com/example/mockmvc/InvokeService.java[tags=invoke-service] ----- -<1> Invoke the root (`/`) of the service and indicate that an `application/json` response is required. -<2> Assert that the service produced the expected response. -<3> Document the call to the service, writing the snippets into a directory named `index` (which is located beneath the configured output directory). -The snippets are written by a `RestDocumentationResultHandler`. -You can obtain an instance of this class from the static `document` method on `org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`. - -[source,java,indent=0,role="secondary"] -.WebTestClient ----- -include::{examples-dir}/com/example/webtestclient/InvokeService.java[tags=invoke-service] ----- -<1> Invoke the root (`/`) of the service and indicate that an `application/json` response is required. -<2> Assert that the service produced the expected response. -<3> Document the call to the service, writing the snippets into a directory named `index` (which is located beneath the configured output directory). -The snippets are written by a `Consumer` of the `ExchangeResult`. -You can obtain such a consumer from the static `document` method on `org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation`. - -[source,java,indent=0,role="secondary"] -.REST Assured ----- -include::{examples-dir}/com/example/restassured/InvokeService.java[tags=invoke-service] ----- -<1> Apply the specification that was initialized in the `@Before` method. -<2> Indicate that an `application/json` response is required. -<3> Document the call to the service, writing the snippets into a directory named `index` (which is located beneath the configured output directory). -The snippets are written by a `RestDocumentationFilter`. -You can obtain an instance of this class from the static `document` method on `RestAssuredRestDocumentation` in the `org.springframework.restdocs.restassured` package. -<4> Invoke the root (`/`) of the service. -<5> Assert that the service produce the expected response. - -By default, six snippets are written: - - * `/index/curl-request.adoc` - * `/index/http-request.adoc` - * `/index/http-response.adoc` - * `/index/httpie-request.adoc` - * `/index/request-body.adoc` - * `/index/response-body.adoc` - -See <> for more information about these and other snippets that can be produced by Spring REST Docs. - - - -[[getting-started-using-the-snippets]] -=== Using the Snippets - -Before using the generated snippets, you must create an `.adoc` source file. -You can name the file whatever you like as long as it has a `.adoc` suffix. -The resulting HTML file has the same name but with a `.html` suffix. -The default location of the source files and the resulting HTML files depends on whether you use Maven or Gradle: - -[cols="2,5,8"] -|=== -| Build tool | Source files | Generated files - -| Maven -| `src/main/asciidoc/*.adoc` -| `target/generated-docs/*.html` - -| Gradle -| `src/docs/asciidoc/*.adoc` -| `build/asciidoc/html5/*.html` -|=== - -You can then include the generated snippets in the manually created Asciidoc file (described earlier in this section) by using the https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#include-files[include macro]. -You can use the `snippets` attribute that is automatically set by `spring-restdocs-asciidoctor` configured in the <> to reference the snippets output directory. -The following example shows how to do so: - -[source,adoc,indent=0] ----- -\include::{snippets}/index/curl-request.adoc[] ----- - - diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc deleted file mode 100644 index c231d8728..000000000 --- a/docs/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,30 +0,0 @@ -= Spring REST Docs -Andy Wilkinson; Jay Bryant -:doctype: book -:icons: font -:source-highlighter: highlightjs -:toc: left -:toclevels: 3 -:sectlinks: - -:examples-dir: ../../test/java -:github: https://github.com/spring-projects/spring-restdocs -:source: {github}/tree/{branch-or-tag} -:samples: https://github.com/spring-projects/spring-restdocs-samples/tree/main -:templates: {source}spring-restdocs/src/main/resources/org/springframework/restdocs/templates -:spring-boot-docs: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle -:spring-framework-docs: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/reference/html/ -:spring-framework-api: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/javadoc-api - -[[abstract]] - -Document RESTful services by combining hand-written documentation with auto-generated snippets produced with Spring MVC Test or WebTestClient. - -include::introduction.adoc[] -include::getting-started.adoc[] -include::documenting-your-api.adoc[] -include::customizing-requests-and-responses.adoc[] -include::configuration.adoc[] -include::working-with-asciidoctor.adoc[] -include::working-with-markdown.adoc[] -include::contributing.adoc[] diff --git a/docs/src/docs/asciidoc/introduction.adoc b/docs/src/docs/asciidoc/introduction.adoc deleted file mode 100644 index eb40443be..000000000 --- a/docs/src/docs/asciidoc/introduction.adoc +++ /dev/null @@ -1,22 +0,0 @@ -[[introduction]] -== Introduction - -The aim of Spring REST Docs is to help you produce accurate and readable documentation for your RESTful services. - -Writing high-quality documentation is difficult. -One way to ease that difficulty is to use tools that are well-suited to the job. -To this end, Spring REST Docs uses https://asciidoctor.org[Asciidoctor] by default. -Asciidoctor processes plain text and produces HTML, styled and laid out to suit your needs. -If you prefer, you can also configure Spring REST Docs to use Markdown. - -Spring REST Docs uses snippets produced by tests written with Spring MVC's {spring-framework-docs}/testing.html#spring-mvc-test-framework[test framework], Spring WebFlux's {spring-framework-docs}/testing.html#webtestclient[`WebTestClient`] or https://rest-assured.io/[REST Assured 5]. -This test-driven approach helps to guarantee the accuracy of your service's documentation. -If a snippet is incorrect, the test that produces it fails. - -Documenting a RESTful service is largely about describing its resources. -Two key parts of each resource's description are the details of the HTTP requests that it consumes and the HTTP responses that it produces. -Spring REST Docs lets you work with these resources and the HTTP requests and responses, shielding your documentation from the inner-details of your service's implementation. -This separation helps you document your service's API rather than its implementation. -It also frees you to evolve the implementation without having to rework the documentation. - - diff --git a/docs/src/docs/asciidoc/working-with-asciidoctor.adoc b/docs/src/docs/asciidoc/working-with-asciidoctor.adoc deleted file mode 100644 index 698d5e460..000000000 --- a/docs/src/docs/asciidoc/working-with-asciidoctor.adoc +++ /dev/null @@ -1,199 +0,0 @@ -[[working-with-asciidoctor]] -== Working with Asciidoctor - -This section describes the aspects of working with Asciidoctor that are particularly relevant to Spring REST Docs. - -NOTE: Asciidoc is the document format. -Asciidoctor is the tool that produces content (usually as HTML) from Asciidoc files (which end with `.adoc`). - - - -[[working-with-asciidoctor-resources]] -=== Resources - - * https://asciidoctor.org/docs/asciidoc-syntax-quick-reference[Syntax quick reference] - * https://asciidoctor.org/docs/user-manual[User manual] - - - -[[working-with-asciidoctor-including-snippets]] -=== Including Snippets - -This section covers how to include Asciidoc snippets. - - - -[[working-with-asciidoctor-including-snippets-operation]] -==== Including Multiple Snippets for an Operation - -You can use a macro named `operation` to import all or some of the snippets that have been generated for a specific operation. -It is made available by including `spring-restdocs-asciidoctor` in your project's <>. -`spring-restdocs-asciidoctor` requires AsciidoctorJ 3.0. - -The target of the macro is the name of the operation. -In its simplest form, you can use the macro to include all of the snippets for an operation, as shown in the following example: - -[source,indent=0] ----- -operation::index[] ----- - -You can use the operation macro also supports a `snippets` attribute. -The `snippets` attribute to select the snippets that should be included. -The attribute's value is a comma-separated list. -Each entry in the list should be the name of a snippet file (minus the `.adoc` suffix) to include. -For example, only the curl, HTTP request, and HTTP response snippets can be included, as shown in the following example: - -[source,indent=0] ----- -operation::index[snippets='curl-request,http-request,http-response'] ----- - -The preceding example is the equivalent of the following: - -[source,adoc,indent=0] ----- -[[example_curl_request]] -== Curl request - -\include::{snippets}/index/curl-request.adoc[] - -[[example_http_request]] -== HTTP request - -\include::{snippets}/index/http-request.adoc[] - -[[example_http_response]] -== HTTP response - -\include::{snippets}/index/http-response.adoc[] - ----- - - - -[[working-with-asciidoctor-including-snippets-operation-titles]] -===== Section Titles - -For each snippet that is included by using the `operation` macro, a section with a title is created. -Default titles are provided for the following built-in snippets: - -|=== -| Snippet | Title - -| `curl-request` -| Curl Request - -| `http-request` -| HTTP request - -| `http-response` -| HTTP response - -| `httpie-request` -| HTTPie request - -| `links` -| Links - -| `request-body` -| Request body - -| `request-fields` -| Request fields - -| `response-body` -| Response body - -| `response-fields` -| Response fields -|=== - -For snippets not listed in the preceding table, a default title is generated by replacing `-` characters with spaces and capitalizing the first letter. -For example, the title for a snippet named `custom-snippet` `will be` "`Custom snippet`". - -You can customize the default titles by using document attributes. -The name of the attribute should be `operation-{snippet}-title`. -For example, to customize the title of the `curl-request` snippet to be "Example request", you can use the following attribute: - -[source,indent=0] ----- -:operation-curl-request-title: Example request ----- - - - -[[working-with-asciidoctor-including-snippets-individual]] -==== Including Individual Snippets - -The https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#include-files[include macro] is used to include individual snippets in your documentation. -You can use the `snippets` attribute (which is automatically set by `spring-restdocs-asciidoctor` configured in the <>) to reference the snippets output directory. -The following example shows how to do so: - -[source,indent=0] ----- -\include::{snippets}/index/curl-request.adoc[] ----- - - - -[[working-with-asciidoctor-customizing-tables]] -=== Customizing Tables - -Many of the snippets contain a table in its default configuration. -The appearance of the table can be customized, either by providing some additional configuration when the snippet is included or by using a custom snippet template. - - - -[[working-with-asciidoctor-customizing-tables-formatting-columns]] -==== Formatting Columns - -Asciidoctor has rich support for https://asciidoctor.org/docs/user-manual/#cols-format[formatting a table's columns]. -As the following example shows, you can specify the widths of a table's columns by using the `cols` attribute: - -[source,indent=0] ----- -[cols="1,3"] <1> -\include::{snippets}/index/links.adoc[] ----- -<1> The table's width is split across its two columns, with the second column being three times as wide as the first. - - - -[[working-with-asciidoctor-customizing-tables-title]] -==== Configuring the Title - -You can specify the title of a table by using a line prefixed by a `.`. -The following example shows how to do so: - -[source,indent=0] ----- -.Links <1> -\include::{snippets}/index/links.adoc[] ----- -<1> The table's title will be `Links`. - - - -[[working-with-asciidoctor-customizing-tables-formatting-problems]] -==== Avoiding Table Formatting Problems - -Asciidoctor uses the `|` character to delimit cells in a table. -This can cause problems if you want a `|` to appear in a cell's contents. -You can avoid the problem by escaping the `|` with a backslash -- in other words, by using `\|` rather than `|`. - -All of the default Asciidoctor snippet templates perform this escaping automatically by using a Mustache lamba named `tableCellContent`. -If you write your own custom templates you may want to use this lamba. -The following example shows how to escape `|` characters in a cell that contains the value of a `description` attribute: - ----- -| {{#tableCellContent}}{{description}}{{/tableCellContent}} ----- - - -[[working-with-asciidoctor-further-reading]] -=== Further Reading - -See the https://asciidoctor.org/docs/user-manual/#tables[Tables section of the Asciidoctor user manual] for more information about customizing tables. - - diff --git a/docs/src/docs/asciidoc/working-with-markdown.adoc b/docs/src/docs/asciidoc/working-with-markdown.adoc deleted file mode 100644 index ed09c0574..000000000 --- a/docs/src/docs/asciidoc/working-with-markdown.adoc +++ /dev/null @@ -1,26 +0,0 @@ -[[working-with-markdown]] -== Working with Markdown - -This section describes the aspects of working with Markdown that are particularly relevant to Spring REST Docs. - - - -[[working-with-markdown-limitations]] -=== Limitations - -Markdown was originally designed for people writing for the web and, as such, is not as well-suited to writing documentation as Asciidoctor. -Typically, these limitations are overcome by using another tool that builds on top of Markdown. - -Markdown has no official support for tables. -Spring REST Docs' default Markdown snippet templates use https://michelf.ca/projects/php-markdown/extra/#table[Markdown Extra's table format]. - - - -[[working-with-markdown-including-snippets]] -=== Including Snippets - -Markdown has no built-in support for including one Markdown file in another. -To include the generated snippets of Markdown in your documentation, you should use an additional tool that supports this functionality. -One example that is particularly well-suited to documenting APIs is https://github.com/tripit/slate[Slate]. - - diff --git a/docs/src/test/java/com/example/Constraints.java b/docs/src/test/java/com/example/Constraints.java deleted file mode 100644 index 6d8ef1fcd..000000000 --- a/docs/src/test/java/com/example/Constraints.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import java.util.List; - -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -import org.springframework.restdocs.constraints.ConstraintDescriptions; - -public class Constraints { - - @SuppressWarnings("unused") - // tag::constraints[] - public void example() { - ConstraintDescriptions userConstraints = new ConstraintDescriptions(UserInput.class); // <1> - List descriptions = userConstraints.descriptionsForProperty("name"); // <2> - } - - static class UserInput { - - @NotNull - @Size(min = 1) - String name; - - @NotNull - @Size(min = 8) - String password; - - } - // end::constraints[] - -} diff --git a/docs/src/test/java/com/example/Hypermedia.java b/docs/src/test/java/com/example/Hypermedia.java deleted file mode 100644 index 041c44aff..000000000 --- a/docs/src/test/java/com/example/Hypermedia.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import org.springframework.restdocs.hypermedia.HypermediaDocumentation; -import org.springframework.restdocs.hypermedia.LinkDescriptor; -import org.springframework.restdocs.hypermedia.LinksSnippet; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; - -public final class Hypermedia { - - private Hypermedia() { - - } - - // tag::ignore-links[] - public static LinksSnippet links(LinkDescriptor... descriptors) { - return HypermediaDocumentation.links(linkWithRel("self").ignored().optional(), linkWithRel("curies").ignored()) - .and(descriptors); - } - // end::ignore-links[] - -} diff --git a/docs/src/test/java/com/example/Payload.java b/docs/src/test/java/com/example/Payload.java deleted file mode 100644 index 321761a35..000000000 --- a/docs/src/test/java/com/example/Payload.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import org.springframework.restdocs.payload.FieldDescriptor; - -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody; - -public class Payload { - - @SuppressWarnings("unused") - public void bookFieldDescriptors() { - // tag::book-descriptors[] - FieldDescriptor[] book = new FieldDescriptor[] { fieldWithPath("title").description("Title of the book"), - fieldWithPath("author").description("Author of the book") }; - // end::book-descriptors[] - } - - public void customSubsectionId() { - // tag::custom-subsection-id[] - responseBody(beneathPath("weather.temperature").withSubsectionId("temp")); - // end::custom-subsection-id[] - } - -} diff --git a/docs/src/test/java/com/example/SnippetReuse.java b/docs/src/test/java/com/example/SnippetReuse.java deleted file mode 100644 index ca87eca03..000000000 --- a/docs/src/test/java/com/example/SnippetReuse.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example; - -import org.springframework.restdocs.hypermedia.LinksSnippet; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; - -public class SnippetReuse { - - // tag::field[] - protected final LinksSnippet pagingLinks = links( - linkWithRel("first").optional().description("The first page of results"), - linkWithRel("last").optional().description("The last page of results"), - linkWithRel("next").optional().description("The next page of results"), - linkWithRel("prev").optional().description("The previous page of results")); - - // end::field[] - -} diff --git a/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java b/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java deleted file mode 100644 index 5aec4248d..000000000 --- a/docs/src/test/java/com/example/mockmvc/CustomDefaultOperationPreprocessors.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; - -@ExtendWith(RestDocumentationExtension.class) -class CustomDefaultOperationPreprocessors { - - private WebApplicationContext context; - - @SuppressWarnings("unused") - private MockMvc mockMvc; - - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - // tag::custom-default-operation-preprocessors[] - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation).operationPreprocessors() - .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> - .withResponseDefaults(prettyPrint())) // <2> - .build(); - // end::custom-default-operation-preprocessors[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java b/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java deleted file mode 100644 index 860e8e1e2..000000000 --- a/docs/src/test/java/com/example/mockmvc/CustomDefaultSnippets.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.cli.CliDocumentation.curlRequest; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomDefaultSnippets { - - @Autowired - private WebApplicationContext context; - - @SuppressWarnings("unused") - private MockMvc mockMvc; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-default-snippets[] - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation).snippets().withDefaults(curlRequest())) - .build(); - // end::custom-default-snippets[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/CustomEncoding.java b/docs/src/test/java/com/example/mockmvc/CustomEncoding.java deleted file mode 100644 index 7b6d1a42d..000000000 --- a/docs/src/test/java/com/example/mockmvc/CustomEncoding.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomEncoding { - - @Autowired - private WebApplicationContext context; - - @SuppressWarnings("unused") - private MockMvc mockMvc; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-encoding[] - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation).snippets().withEncoding("ISO-8859-1")) - .build(); - // end::custom-encoding[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/CustomFormat.java b/docs/src/test/java/com/example/mockmvc/CustomFormat.java deleted file mode 100644 index 75a304654..000000000 --- a/docs/src/test/java/com/example/mockmvc/CustomFormat.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomFormat { - - @Autowired - private WebApplicationContext context; - - @SuppressWarnings("unused") - private MockMvc mockMvc; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-format[] - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation).snippets() - .withTemplateFormat(TemplateFormats.markdown())) - .build(); - // end::custom-format[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java b/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java deleted file mode 100644 index 7af440b08..000000000 --- a/docs/src/test/java/com/example/mockmvc/CustomUriConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomUriConfiguration { - - @Autowired - private WebApplicationContext context; - - @SuppressWarnings("unused") - private MockMvc mockMvc; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-uri-configuration[] - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation).uris() - .withScheme("https") - .withHost("example.com") - .withPort(443)) - .build(); - // end::custom-uri-configuration[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java b/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java deleted file mode 100644 index dcf4d44af..000000000 --- a/docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@ExtendWith(RestDocumentationExtension.class) -public class EveryTestPreprocessing { - - private WebApplicationContext context; - - // tag::setup[] - private MockMvc mockMvc; - - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation).operationPreprocessors() - .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> - .withResponseDefaults(prettyPrint())) // <2> - .build(); - } - // end::setup[] - - public void use() throws Exception { - // tag::use[] - this.mockMvc.perform(get("/")) - .andExpect(status().isOk()) - .andDo(document("index", links(linkWithRel("self").description("Canonical self link")))); - // end::use[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/ExampleApplicationTestNgTests.java b/docs/src/test/java/com/example/mockmvc/ExampleApplicationTestNgTests.java deleted file mode 100644 index 4b3eb6173..000000000 --- a/docs/src/test/java/com/example/mockmvc/ExampleApplicationTestNgTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import java.lang.reflect.Method; - -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.restdocs.ManualRestDocumentation; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -public class ExampleApplicationTestNgTests { - - public final ManualRestDocumentation restDocumentation = new ManualRestDocumentation(); - - @SuppressWarnings("unused") - // tag::setup[] - private MockMvc mockMvc; - - @Autowired - private WebApplicationContext context; - - @BeforeMethod - public void setUp(Method method) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - this.restDocumentation.beforeTest(getClass(), method.getName()); - } - - // end::setup[] - - // tag::teardown[] - @AfterMethod - public void tearDown() { - this.restDocumentation.afterTest(); - } - // end::teardown[] - -} diff --git a/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java b/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java deleted file mode 100644 index 24c0a3f4b..000000000 --- a/docs/src/test/java/com/example/mockmvc/ExampleApplicationTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ExampleApplicationTests { - - @SuppressWarnings("unused") - // tag::setup[] - private MockMvc mockMvc; - - @BeforeEach - void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) - .apply(documentationConfiguration(restDocumentation)) // <1> - .build(); - } - // end::setup[] - -} diff --git a/docs/src/test/java/com/example/mockmvc/FormParameters.java b/docs/src/test/java/com/example/mockmvc/FormParameters.java deleted file mode 100644 index ff1472c1c..000000000 --- a/docs/src/test/java/com/example/mockmvc/FormParameters.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.request.RequestDocumentation.formParameters; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class FormParameters { - - private MockMvc mockMvc; - - public void postFormDataSnippet() throws Exception { - // tag::form-parameters[] - this.mockMvc.perform(post("/users").param("username", "Tester")) // <1> - .andExpect(status().isCreated()) - .andDo(document("create-user", formParameters(// <2> - parameterWithName("username").description("The user's username") // <3> - ))); - // end::form-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/HttpCookies.java b/docs/src/test/java/com/example/mockmvc/HttpCookies.java deleted file mode 100644 index ad609bf59..000000000 --- a/docs/src/test/java/com/example/mockmvc/HttpCookies.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import jakarta.servlet.http.Cookie; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; -import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; -import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class HttpCookies { - - private MockMvc mockMvc; - - public void cookies() throws Exception { - // tag::cookies[] - this.mockMvc.perform(get("/").cookie(new Cookie("JSESSIONID", "ACBCDFD0FF93D5BB"))) // <1> - .andExpect(status().isOk()) - .andDo(document("cookies", requestCookies(// <2> - cookieWithName("JSESSIONID").description("Session token")), // <3> - responseCookies(// <4> - cookieWithName("JSESSIONID").description("Updated session token"), - cookieWithName("logged_in") - .description("Set to true if the user is currently logged in")))); - // end::cookies[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/HttpHeaders.java b/docs/src/test/java/com/example/mockmvc/HttpHeaders.java deleted file mode 100644 index 1395d6366..000000000 --- a/docs/src/test/java/com/example/mockmvc/HttpHeaders.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class HttpHeaders { - - private MockMvc mockMvc; - - public void headers() throws Exception { - // tag::headers[] - this.mockMvc.perform(get("/people").header("Authorization", "Basic dXNlcjpzZWNyZXQ=")) // <1> - .andExpect(status().isOk()) - .andDo(document("headers", requestHeaders(// <2> - headerWithName("Authorization").description("Basic auth credentials")), // <3> - responseHeaders(// <4> - headerWithName("X-RateLimit-Limit") - .description("The total number of requests permitted per period"), - headerWithName("X-RateLimit-Remaining") - .description("Remaining requests permitted in current period"), - headerWithName("X-RateLimit-Reset") - .description("Time at which the rate limit period will reset")))); - // end::headers[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/Hypermedia.java b/docs/src/test/java/com/example/mockmvc/Hypermedia.java deleted file mode 100644 index 5993f900e..000000000 --- a/docs/src/test/java/com/example/mockmvc/Hypermedia.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class Hypermedia { - - private MockMvc mockMvc; - - public void defaultExtractor() throws Exception { - // tag::links[] - this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("index", links(// <1> - linkWithRel("alpha").description("Link to the alpha resource"), // <2> - linkWithRel("bravo").description("Link to the bravo resource")))); // <3> - // end::links[] - } - - public void explicitExtractor() throws Exception { - this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - // tag::explicit-extractor[] - .andDo(document("index", links(halLinks(), // <1> - linkWithRel("alpha").description("Link to the alpha resource"), - linkWithRel("bravo").description("Link to the bravo resource")))); - // end::explicit-extractor[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/InvokeService.java b/docs/src/test/java/com/example/mockmvc/InvokeService.java deleted file mode 100644 index bd8d265af..000000000 --- a/docs/src/test/java/com/example/mockmvc/InvokeService.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class InvokeService { - - private MockMvc mockMvc; - - public void invokeService() throws Exception { - // tag::invoke-service[] - this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) // <1> - .andExpect(status().isOk()) // <2> - .andDo(document("index")); // <3> - // end::invoke-service[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/MockMvcSnippetReuse.java b/docs/src/test/java/com/example/mockmvc/MockMvcSnippetReuse.java deleted file mode 100644 index a81f28275..000000000 --- a/docs/src/test/java/com/example/mockmvc/MockMvcSnippetReuse.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import com.example.SnippetReuse; - -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class MockMvcSnippetReuse extends SnippetReuse { - - private MockMvc mockMvc; - - public void documentation() throws Exception { - // tag::use[] - this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("example", this.pagingLinks.and(// <1> - linkWithRel("alpha").description("Link to the alpha resource"), - linkWithRel("bravo").description("Link to the bravo resource")))); - // end::use[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java b/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java deleted file mode 100644 index 831c9a4ae..000000000 --- a/docs/src/test/java/com/example/mockmvc/ParameterizedOutput.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ParameterizedOutput { - - @SuppressWarnings("unused") - private MockMvc mockMvc; - - private WebApplicationContext context; - - // tag::parameterized-output[] - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(restDocumentation)) - .alwaysDo(document("{method-name}/{step}/")) - .build(); - } - // end::parameterized-output[] - -} diff --git a/docs/src/test/java/com/example/mockmvc/PathParameters.java b/docs/src/test/java/com/example/mockmvc/PathParameters.java deleted file mode 100644 index 22135e60b..000000000 --- a/docs/src/test/java/com/example/mockmvc/PathParameters.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class PathParameters { - - private MockMvc mockMvc; - - public void pathParametersSnippet() throws Exception { - // tag::path-parameters[] - this.mockMvc.perform(get("/locations/{latitude}/{longitude}", 51.5072, 0.1275)) // <1> - .andExpect(status().isOk()) - .andDo(document("locations", pathParameters(// <2> - parameterWithName("latitude").description("The location's latitude"), // <3> - parameterWithName("longitude").description("The location's longitude") // <4> - ))); - // end::path-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/Payload.java b/docs/src/test/java/com/example/mockmvc/Payload.java deleted file mode 100644 index 3ce122d7d..000000000 --- a/docs/src/test/java/com/example/mockmvc/Payload.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.FieldDescriptor; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class Payload { - - private MockMvc mockMvc; - - public void response() throws Exception { - // tag::response[] - this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("index", responseFields(// <1> - fieldWithPath("contact.email").description("The user's email address"), // <2> - fieldWithPath("contact.name").description("The user's name")))); // <3> - // end::response[] - } - - public void subsection() throws Exception { - // tag::subsection[] - this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("index", responseFields(// <1> - subsectionWithPath("contact").description("The user's contact details")))); // <1> - // end::subsection[] - } - - public void explicitType() throws Exception { - this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - // tag::explicit-type[] - .andDo(document("index", responseFields(fieldWithPath("contact.email").type(JsonFieldType.STRING) // <1> - .description("The user's email address")))); - // end::explicit-type[] - } - - public void constraints() throws Exception { - this.mockMvc.perform(post("/users/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - // tag::constraints[] - .andDo(document("create-user", requestFields(attributes(key("title").value("Fields for user creation")), // <1> - fieldWithPath("name").description("The user's name") - .attributes(key("constraints").value("Must not be null. Must not be empty")), // <2> - fieldWithPath("email").description("The user's email address") - .attributes(key("constraints").value("Must be a valid email address"))))); // <3> - // end::constraints[] - } - - public void descriptorReuse() throws Exception { - FieldDescriptor[] book = new FieldDescriptor[] { fieldWithPath("title").description("Title of the book"), - fieldWithPath("author").description("Author of the book") }; - - // tag::single-book[] - this.mockMvc.perform(get("/books/1").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("book", responseFields(book))); // <1> - // end::single-book[] - - // tag::book-array[] - this.mockMvc.perform(get("/books").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("book", responseFields(fieldWithPath("[]").description("An array of books")) // <1> - .andWithPrefix("[].", book))); // <2> - // end::book-array[] - } - - public void fieldsSubsection() throws Exception { - // tag::fields-subsection[] - this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("location", responseFields(beneathPath("weather.temperature"), // <1> - fieldWithPath("high").description("The forecast high in degrees celcius"), // <2> - fieldWithPath("low").description("The forecast low in degrees celcius")))); - // end::fields-subsection[] - } - - public void bodySubsection() throws Exception { - // tag::body-subsection[] - this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("location", responseBody(beneathPath("weather.temperature")))); // <1> - - // end::body-subsection[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/PerTestPreprocessing.java b/docs/src/test/java/com/example/mockmvc/PerTestPreprocessing.java deleted file mode 100644 index 75f91610e..000000000 --- a/docs/src/test/java/com/example/mockmvc/PerTestPreprocessing.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class PerTestPreprocessing { - - private MockMvc mockMvc; - - public void general() throws Exception { - // tag::preprocessing[] - this.mockMvc.perform(get("/")) - .andExpect(status().isOk()) - .andDo(document("index", preprocessRequest(modifyHeaders().remove("Foo")), // <1> - preprocessResponse(prettyPrint()))); // <2> - // end::preprocessing[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/QueryParameters.java b/docs/src/test/java/com/example/mockmvc/QueryParameters.java deleted file mode 100644 index 68c60f917..000000000 --- a/docs/src/test/java/com/example/mockmvc/QueryParameters.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class QueryParameters { - - private MockMvc mockMvc; - - public void getQueryStringSnippet() throws Exception { - // tag::query-parameters[] - this.mockMvc.perform(get("/users?page=2&per_page=100")) // <1> - .andExpect(status().isOk()) - .andDo(document("users", queryParameters(// <2> - parameterWithName("page").description("The page to retrieve"), // <3> - parameterWithName("per_page").description("Entries per page") // <4> - ))); - // end::query-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/RequestPartPayload.java b/docs/src/test/java/com/example/mockmvc/RequestPartPayload.java deleted file mode 100644 index 201ad3b41..000000000 --- a/docs/src/test/java/com/example/mockmvc/RequestPartPayload.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.multipart; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class RequestPartPayload { - - private MockMvc mockMvc; - - public void fields() throws Exception { - // tag::fields[] - MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png", "<>".getBytes()); - MockMultipartFile metadata = new MockMultipartFile("metadata", "", "application/json", - "{ \"version\": \"1.0\"}".getBytes()); - - this.mockMvc.perform(multipart("/images").file(image).file(metadata).accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("image-upload", requestPartFields("metadata", // <1> - fieldWithPath("version").description("The version of the image")))); // <2> - // end::fields[] - } - - public void body() throws Exception { - // tag::body[] - MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png", "<>".getBytes()); - MockMultipartFile metadata = new MockMultipartFile("metadata", "", "application/json", - "{ \"version\": \"1.0\"}".getBytes()); - - this.mockMvc.perform(multipart("/images").file(image).file(metadata).accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("image-upload", requestPartBody("metadata"))); // <1> - // end::body[] - } - -} diff --git a/docs/src/test/java/com/example/mockmvc/RequestParts.java b/docs/src/test/java/com/example/mockmvc/RequestParts.java deleted file mode 100644 index 19f0cb0e8..000000000 --- a/docs/src/test/java/com/example/mockmvc/RequestParts.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.mockmvc; - -import org.springframework.test.web.servlet.MockMvc; - -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.request.RequestDocumentation.requestParts; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class RequestParts { - - private MockMvc mockMvc; - - public void upload() throws Exception { - // tag::request-parts[] - this.mockMvc.perform(multipart("/upload").file("file", "example".getBytes())) // <1> - .andExpect(status().isOk()) - .andDo(document("upload", requestParts(// <2> - partWithName("file").description("The file to upload")) // <3> - )); - // end::request-parts[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java b/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java deleted file mode 100644 index 2d0fa772d..000000000 --- a/docs/src/test/java/com/example/restassured/CustomDefaultOperationPreprocessors.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomDefaultOperationPreprocessors { - - @SuppressWarnings("unused") - private RequestSpecification spec; - - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - // tag::custom-default-operation-preprocessors[] - this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(restDocumentation).operationPreprocessors() - .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> - .withResponseDefaults(prettyPrint())) // <2> - .build(); - // end::custom-default-operation-preprocessors[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java b/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java deleted file mode 100644 index 3b6d175f1..000000000 --- a/docs/src/test/java/com/example/restassured/CustomDefaultSnippets.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.springframework.restdocs.cli.CliDocumentation.curlRequest; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomDefaultSnippets { - - @SuppressWarnings("unused") - private RequestSpecification spec; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-default-snippets[] - this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(restDocumentation).snippets().withDefaults(curlRequest())) - .build(); - // end::custom-default-snippets[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/CustomEncoding.java b/docs/src/test/java/com/example/restassured/CustomEncoding.java deleted file mode 100644 index 316b19a21..000000000 --- a/docs/src/test/java/com/example/restassured/CustomEncoding.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomEncoding { - - @SuppressWarnings("unused") - private RequestSpecification spec; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-encoding[] - this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(restDocumentation).snippets().withEncoding("ISO-8859-1")) - .build(); - // end::custom-encoding[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/CustomFormat.java b/docs/src/test/java/com/example/restassured/CustomFormat.java deleted file mode 100644 index 5f690f1b3..000000000 --- a/docs/src/test/java/com/example/restassured/CustomFormat.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.templates.TemplateFormats; - -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomFormat { - - @SuppressWarnings("unused") - private RequestSpecification spec; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-format[] - this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(restDocumentation).snippets() - .withTemplateFormat(TemplateFormats.markdown())) - .build(); - // end::custom-format[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java b/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java deleted file mode 100644 index 741cfdfe9..000000000 --- a/docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class EveryTestPreprocessing { - - // tag::setup[] - private RequestSpecification spec; - - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - this.spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(restDocumentation).operationPreprocessors() - .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> - .withResponseDefaults(prettyPrint())) // <2> - .build(); - } - // end::setup[] - - void use() { - // tag::use[] - RestAssured.given(this.spec) - .filter(document("index", links(linkWithRel("self").description("Canonical self link")))) - .when() - .get("/") - .then() - .assertThat() - .statusCode(is(200)); - // end::use[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/ExampleApplicationTestNgTests.java b/docs/src/test/java/com/example/restassured/ExampleApplicationTestNgTests.java deleted file mode 100644 index d1e07b215..000000000 --- a/docs/src/test/java/com/example/restassured/ExampleApplicationTestNgTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import java.lang.reflect.Method; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; - -import org.springframework.restdocs.ManualRestDocumentation; - -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -public class ExampleApplicationTestNgTests { - - private final ManualRestDocumentation restDocumentation = new ManualRestDocumentation(); - - @SuppressWarnings("unused") - // tag::setup[] - private RequestSpecification spec; - - @BeforeMethod - public void setUp(Method method) { - this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(this.restDocumentation)).build(); - this.restDocumentation.beforeTest(getClass(), method.getName()); - } - - // end::setup[] - - // tag::teardown[] - @AfterMethod - public void tearDown() { - this.restDocumentation.afterTest(); - } - // end::teardown[] - -} diff --git a/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java b/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java deleted file mode 100644 index 7affb1ef2..000000000 --- a/docs/src/test/java/com/example/restassured/ExampleApplicationTests.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ExampleApplicationTests { - - @SuppressWarnings("unused") - // tag::setup[] - private RequestSpecification spec; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(restDocumentation)) // <1> - .build(); - } - // end::setup[] - -} diff --git a/docs/src/test/java/com/example/restassured/FormParameters.java b/docs/src/test/java/com/example/restassured/FormParameters.java deleted file mode 100644 index 88a720f14..000000000 --- a/docs/src/test/java/com/example/restassured/FormParameters.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.request.RequestDocumentation.formParameters; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class FormParameters { - - private RequestSpecification spec; - - public void postFormDataSnippet() { - // tag::form-parameters[] - RestAssured.given(this.spec) - .filter(document("create-user", formParameters(// <1> - parameterWithName("username").description("The user's username")))) // <2> - .formParam("username", "Tester") - .when() - .post("/users") // <3> - .then() - .assertThat() - .statusCode(is(200)); - // end::form-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/HttpCookies.java b/docs/src/test/java/com/example/restassured/HttpCookies.java deleted file mode 100644 index 3ad4993ca..000000000 --- a/docs/src/test/java/com/example/restassured/HttpCookies.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; -import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; -import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class HttpCookies { - - private RequestSpecification spec; - - public void cookies() { - // tag::cookies[] - RestAssured.given(this.spec) - .filter(document("cookies", requestCookies(// <1> - cookieWithName("JSESSIONID").description("Saved session token")), // <2> - responseCookies(// <3> - cookieWithName("logged_in").description("If user is logged in"), - cookieWithName("JSESSIONID").description("Updated session token")))) - .cookie("JSESSIONID", "ACBCDFD0FF93D5BB") // <4> - .when() - .get("/people") - .then() - .assertThat() - .statusCode(is(200)); - // end::cookies[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/HttpHeaders.java b/docs/src/test/java/com/example/restassured/HttpHeaders.java deleted file mode 100644 index 4595c351c..000000000 --- a/docs/src/test/java/com/example/restassured/HttpHeaders.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class HttpHeaders { - - private RequestSpecification spec; - - public void headers() { - // tag::headers[] - RestAssured.given(this.spec) - .filter(document("headers", requestHeaders(// <1> - headerWithName("Authorization").description("Basic auth credentials")), // <2> - responseHeaders(// <3> - headerWithName("X-RateLimit-Limit") - .description("The total number of requests permitted per period"), - headerWithName("X-RateLimit-Remaining") - .description("Remaining requests permitted in current period"), - headerWithName("X-RateLimit-Reset") - .description("Time at which the rate limit period will reset")))) - .header("Authorization", "Basic dXNlcjpzZWNyZXQ=") // <4> - .when() - .get("/people") - .then() - .assertThat() - .statusCode(is(200)); - // end::headers[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/Hypermedia.java b/docs/src/test/java/com/example/restassured/Hypermedia.java deleted file mode 100644 index 410daca80..000000000 --- a/docs/src/test/java/com/example/restassured/Hypermedia.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class Hypermedia { - - private RequestSpecification spec; - - public void defaultExtractor() { - // tag::links[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("index", links(// <1> - linkWithRel("alpha").description("Link to the alpha resource"), // <2> - linkWithRel("bravo").description("Link to the bravo resource")))) // <3> - .get("/") - .then() - .assertThat() - .statusCode(is(200)); - // end::links[] - } - - public void explicitExtractor() { - RestAssured.given(this.spec) - .accept("application/json") - // tag::explicit-extractor[] - .filter(document("index", links(halLinks(), // <1> - linkWithRel("alpha").description("Link to the alpha resource"), - linkWithRel("bravo").description("Link to the bravo resource")))) - // end::explicit-extractor[] - .get("/") - .then() - .assertThat() - .statusCode(is(200)); - } - -} diff --git a/docs/src/test/java/com/example/restassured/InvokeService.java b/docs/src/test/java/com/example/restassured/InvokeService.java deleted file mode 100644 index 8d5de1b7d..000000000 --- a/docs/src/test/java/com/example/restassured/InvokeService.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class InvokeService { - - private RequestSpecification spec; - - public void invokeService() { - // tag::invoke-service[] - RestAssured.given(this.spec) // <1> - .accept("application/json") // <2> - .filter(document("index")) // <3> - .when() - .get("/") // <4> - .then() - .assertThat() - .statusCode(is(200)); // <5> - // end::invoke-service[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/ParameterizedOutput.java b/docs/src/test/java/com/example/restassured/ParameterizedOutput.java deleted file mode 100644 index 7471d1772..000000000 --- a/docs/src/test/java/com/example/restassured/ParameterizedOutput.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; - -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -public class ParameterizedOutput { - - @SuppressWarnings("unused") - private RequestSpecification spec; - - // tag::parameterized-output[] - @BeforeEach - public void setUp(RestDocumentationContextProvider restDocumentation) { - this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(restDocumentation)) - .addFilter(document("{method-name}/{step}")) - .build(); - } - // end::parameterized-output[] - -} diff --git a/docs/src/test/java/com/example/restassured/PathParameters.java b/docs/src/test/java/com/example/restassured/PathParameters.java deleted file mode 100644 index dc2f3306b..000000000 --- a/docs/src/test/java/com/example/restassured/PathParameters.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class PathParameters { - - private RequestSpecification spec; - - public void pathParametersSnippet() { - // tag::path-parameters[] - RestAssured.given(this.spec) - .filter(document("locations", pathParameters(// <1> - parameterWithName("latitude").description("The location's latitude"), // <2> - parameterWithName("longitude").description("The location's longitude")))) // <3> - .when() - .get("/locations/{latitude}/{longitude}", 51.5072, 0.1275) // <4> - .then() - .assertThat() - .statusCode(is(200)); - // end::path-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/Payload.java b/docs/src/test/java/com/example/restassured/Payload.java deleted file mode 100644 index 6c90f2299..000000000 --- a/docs/src/test/java/com/example/restassured/Payload.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import org.springframework.restdocs.payload.FieldDescriptor; -import org.springframework.restdocs.payload.JsonFieldType; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -public class Payload { - - private RequestSpecification spec; - - public void response() { - // tag::response[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("user", responseFields(// <1> - fieldWithPath("contact.name").description("The user's name"), // <2> - fieldWithPath("contact.email").description("The user's email address")))) // <3> - .when() - .get("/user/5") - .then() - .assertThat() - .statusCode(is(200)); - // end::response[] - } - - public void subsection() { - // tag::subsection[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("user", - responseFields(subsectionWithPath("contact").description("The user's contact details")))) // <1> - .when() - .get("/user/5") - .then() - .assertThat() - .statusCode(is(200)); - // end::subsection[] - } - - public void explicitType() { - RestAssured.given(this.spec) - .accept("application/json") - // tag::explicit-type[] - .filter(document("user", responseFields(fieldWithPath("contact.email").type(JsonFieldType.STRING) // <1> - .description("The user's email address")))) - // end::explicit-type[] - .when() - .get("/user/5") - .then() - .assertThat() - .statusCode(is(200)); - } - - public void constraints() { - RestAssured.given(this.spec) - .accept("application/json") - // tag::constraints[] - .filter(document("create-user", requestFields(attributes(key("title").value("Fields for user creation")), // <1> - fieldWithPath("name").description("The user's name") - .attributes(key("constraints").value("Must not be null. Must not be empty")), // <2> - fieldWithPath("email").description("The user's email address") - .attributes(key("constraints").value("Must be a valid email address"))))) // <3> - // end::constraints[] - .when() - .post("/users") - .then() - .assertThat() - .statusCode(is(200)); - } - - public void descriptorReuse() { - FieldDescriptor[] book = new FieldDescriptor[] { fieldWithPath("title").description("Title of the book"), - fieldWithPath("author").description("Author of the book") }; - - // tag::single-book[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("book", responseFields(book))) // <1> - .when() - .get("/books/1") - .then() - .assertThat() - .statusCode(is(200)); - // end::single-book[] - - // tag::book-array[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("books", responseFields(fieldWithPath("[]").description("An array of books")) // <1> - .andWithPrefix("[].", book))) // <2> - .when() - .get("/books") - .then() - .assertThat() - .statusCode(is(200)); - // end::book-array[] - } - - public void fieldsSubsection() { - // tag::fields-subsection[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("location", responseFields(beneathPath("weather.temperature"), // <1> - fieldWithPath("high").description("The forecast high in degrees celcius"), // <2> - fieldWithPath("low").description("The forecast low in degrees celcius")))) - .when() - .get("/locations/1") - .then() - .assertThat() - .statusCode(is(200)); - // end::fields-subsection[] - } - - public void bodySubsection() { - // tag::body-subsection[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("location", responseBody(beneathPath("weather.temperature")))) // <1> - .when() - .get("/locations/1") - .then() - .assertThat() - .statusCode(is(200)); - // end::body-subsection[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/PerTestPreprocessing.java b/docs/src/test/java/com/example/restassured/PerTestPreprocessing.java deleted file mode 100644 index eeb937af5..000000000 --- a/docs/src/test/java/com/example/restassured/PerTestPreprocessing.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class PerTestPreprocessing { - - private RequestSpecification spec; - - public void general() { - // tag::preprocessing[] - RestAssured.given(this.spec) - .filter(document("index", preprocessRequest(modifyHeaders().remove("Foo")), // <1> - preprocessResponse(prettyPrint()))) // <2> - .when() - .get("/") - .then() - .assertThat() - .statusCode(is(200)); - // end::preprocessing[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/QueryParameters.java b/docs/src/test/java/com/example/restassured/QueryParameters.java deleted file mode 100644 index 27488ed93..000000000 --- a/docs/src/test/java/com/example/restassured/QueryParameters.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class QueryParameters { - - private RequestSpecification spec; - - public void getQueryStringSnippet() { - // tag::query-parameters[] - RestAssured.given(this.spec) - .filter(document("users", queryParameters(// <1> - parameterWithName("page").description("The page to retrieve"), // <2> - parameterWithName("per_page").description("Entries per page")))) // <3> - .when() - .get("/users?page=2&per_page=100") // <4> - .then() - .assertThat() - .statusCode(is(200)); - // end::query-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/RequestPartPayload.java b/docs/src/test/java/com/example/restassured/RequestPartPayload.java deleted file mode 100644 index 2566c37a9..000000000 --- a/docs/src/test/java/com/example/restassured/RequestPartPayload.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class RequestPartPayload { - - private RequestSpecification spec; - - public void fields() { - // tag::fields[] - Map metadata = new HashMap<>(); - metadata.put("version", "1.0"); - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("image-upload", requestPartFields("metadata", // <1> - fieldWithPath("version").description("The version of the image")))) // <2> - .when() - .multiPart("image", new File("image.png"), "image/png") - .multiPart("metadata", metadata) - .post("images") - .then() - .assertThat() - .statusCode(is(200)); - // end::fields[] - } - - public void body() { - // tag::body[] - Map metadata = new HashMap<>(); - metadata.put("version", "1.0"); - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("image-upload", requestPartBody("metadata"))) // <1> - .when() - .multiPart("image", new File("image.png"), "image/png") - .multiPart("metadata", metadata) - .post("images") - .then() - .assertThat() - .statusCode(is(200)); - // end::body[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/RequestParts.java b/docs/src/test/java/com/example/restassured/RequestParts.java deleted file mode 100644 index ee3547367..000000000 --- a/docs/src/test/java/com/example/restassured/RequestParts.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.request.RequestDocumentation.requestParts; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class RequestParts { - - private RequestSpecification spec; - - public void upload() { - // tag::request-parts[] - RestAssured.given(this.spec) - .filter(document("users", requestParts(// <1> - partWithName("file").description("The file to upload")))) // <2> - .multiPart("file", "example") // <3> - .when() - .post("/upload") // <4> - .then() - .statusCode(is(200)); - // end::request-parts[] - } - -} diff --git a/docs/src/test/java/com/example/restassured/RestAssuredSnippetReuse.java b/docs/src/test/java/com/example/restassured/RestAssuredSnippetReuse.java deleted file mode 100644 index 800903a00..000000000 --- a/docs/src/test/java/com/example/restassured/RestAssuredSnippetReuse.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.restassured; - -import com.example.SnippetReuse; -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; - -import static org.hamcrest.CoreMatchers.is; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; - -public class RestAssuredSnippetReuse extends SnippetReuse { - - private RequestSpecification spec; - - public void documentation() { - // tag::use[] - RestAssured.given(this.spec) - .accept("application/json") - .filter(document("example", this.pagingLinks.and(// <1> - linkWithRel("alpha").description("Link to the alpha resource"), - linkWithRel("bravo").description("Link to the bravo resource")))) - .get("/") - .then() - .assertThat() - .statusCode(is(200)); - // end::use[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java b/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java deleted file mode 100644 index 17ee06b44..000000000 --- a/docs/src/test/java/com/example/webtestclient/CustomDefaultOperationPreprocessors.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomDefaultOperationPreprocessors { - - // @formatter:off - - private ApplicationContext context; - - @SuppressWarnings("unused") - private WebTestClient webTestClient; - - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - // tag::custom-default-operation-preprocessors[] - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) - .configureClient() - .filter(documentationConfiguration(restDocumentation) - .operationPreprocessors() - .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> - .withResponseDefaults(prettyPrint())) // <2> - .build(); - // end::custom-default-operation-preprocessors[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java b/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java deleted file mode 100644 index 420a52f49..000000000 --- a/docs/src/test/java/com/example/webtestclient/CustomDefaultSnippets.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.cli.CliDocumentation.curlRequest; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomDefaultSnippets { - - // @formatter:off - - @Autowired - private ApplicationContext context; - - @SuppressWarnings("unused") - private WebTestClient webTestClient; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-default-snippets[] - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) - .configureClient().filter( - documentationConfiguration(restDocumentation) - .snippets().withDefaults(curlRequest())) - .build(); - // end::custom-default-snippets[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/CustomEncoding.java b/docs/src/test/java/com/example/webtestclient/CustomEncoding.java deleted file mode 100644 index 42800d797..000000000 --- a/docs/src/test/java/com/example/webtestclient/CustomEncoding.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomEncoding { - - // @formatter:off - - @Autowired - private ApplicationContext context; - - @SuppressWarnings("unused") - private WebTestClient webTestClient; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-encoding[] - this.webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient() - .filter(documentationConfiguration(restDocumentation) - .snippets().withEncoding("ISO-8859-1")) - .build(); - // end::custom-encoding[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/CustomFormat.java b/docs/src/test/java/com/example/webtestclient/CustomFormat.java deleted file mode 100644 index ab57f1846..000000000 --- a/docs/src/test/java/com/example/webtestclient/CustomFormat.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomFormat { - - // @formatter:off - - @Autowired - private ApplicationContext context; - - @SuppressWarnings("unused") - private WebTestClient webTestClient; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - // tag::custom-format[] - this.webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient() - .filter(documentationConfiguration(restDocumentation) - .snippets().withTemplateFormat(TemplateFormats.markdown())) - .build(); - // end::custom-format[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java b/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java deleted file mode 100644 index 9db88cded..000000000 --- a/docs/src/test/java/com/example/webtestclient/CustomUriConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class CustomUriConfiguration { - - @SuppressWarnings("unused") - private WebTestClient webTestClient; - - @Autowired - private ApplicationContext context; - - // tag::custom-uri-configuration[] - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) - .configureClient() - .baseUrl("https://api.example.com") // <1> - .filter(documentationConfiguration(restDocumentation)) - .build(); - } - // end::custom-uri-configuration[] - -} diff --git a/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java b/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java deleted file mode 100644 index e6ff7ceb9..000000000 --- a/docs/src/test/java/com/example/webtestclient/EveryTestPreprocessing.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class EveryTestPreprocessing { - - // @formatter:off - - private ApplicationContext context; - - // tag::setup[] - private WebTestClient webTestClient; - - @BeforeEach - void setup(RestDocumentationContextProvider restDocumentation) { - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) - .configureClient() - .filter(documentationConfiguration(restDocumentation) - .operationPreprocessors() - .withRequestDefaults(modifyHeaders().remove("Foo")) // <1> - .withResponseDefaults(prettyPrint())) // <2> - .build(); - } - // end::setup[] - - void use() { - // tag::use[] - this.webTestClient.get().uri("/").exchange().expectStatus().isOk() - .expectBody().consumeWith(document("index", - links(linkWithRel("self").description("Canonical self link")))); - // end::use[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/ExampleApplicationTestNgTests.java b/docs/src/test/java/com/example/webtestclient/ExampleApplicationTestNgTests.java deleted file mode 100644 index 7e905b0c2..000000000 --- a/docs/src/test/java/com/example/webtestclient/ExampleApplicationTestNgTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import java.lang.reflect.Method; - -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.ManualRestDocumentation; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -public class ExampleApplicationTestNgTests { - - public final ManualRestDocumentation restDocumentation = new ManualRestDocumentation(); - - @SuppressWarnings("unused") - // tag::setup[] - private WebTestClient webTestClient; - - @Autowired - private ApplicationContext context; - - @BeforeMethod - public void setUp(Method method) { - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) - .configureClient() - .filter(documentationConfiguration(this.restDocumentation)) // <1> - .build(); - this.restDocumentation.beforeTest(getClass(), method.getName()); - } - - // end::setup[] - - // tag::teardown[] - @AfterMethod - public void tearDown() { - this.restDocumentation.afterTest(); - } - // end::teardown[] - -} diff --git a/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java b/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java deleted file mode 100644 index 3126487ad..000000000 --- a/docs/src/test/java/com/example/webtestclient/ExampleApplicationTests.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ExampleApplicationTests { - - @SuppressWarnings("unused") - // tag::setup[] - private WebTestClient webTestClient; - - @BeforeEach - void setUp(ApplicationContext applicationContext, RestDocumentationContextProvider restDocumentation) { - this.webTestClient = WebTestClient.bindToApplicationContext(applicationContext) - .configureClient() - .filter(documentationConfiguration(restDocumentation)) // <1> - .build(); - } - // end::setup[] - -} diff --git a/docs/src/test/java/com/example/webtestclient/FormParameters.java b/docs/src/test/java/com/example/webtestclient/FormParameters.java deleted file mode 100644 index 0bfdbc0c1..000000000 --- a/docs/src/test/java/com/example/webtestclient/FormParameters.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserters; - -import static org.springframework.restdocs.request.RequestDocumentation.formParameters; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class FormParameters { - - // @formatter:off - - private WebTestClient webTestClient; - - public void postFormDataSnippet() { - // tag::form-parameters[] - MultiValueMap formData = new LinkedMultiValueMap<>(); - formData.add("username", "Tester"); - this.webTestClient.post().uri("/users").body(BodyInserters.fromFormData(formData)) // <1> - .exchange().expectStatus().isCreated().expectBody() - .consumeWith(document("create-user", formParameters(// <2> - parameterWithName("username").description("The user's username") // <3> - ))); - // end::form-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/HttpCookies.java b/docs/src/test/java/com/example/webtestclient/HttpCookies.java deleted file mode 100644 index 1db0fe475..000000000 --- a/docs/src/test/java/com/example/webtestclient/HttpCookies.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; -import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; -import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class HttpCookies { - - private WebTestClient webTestClient; - - public void cookies() { - // tag::cookies[] - this.webTestClient.get() - .uri("/people") - .cookie("JSESSIONID", "ACBCDFD0FF93D5BB=") // <1> - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("cookies", requestCookies(// <2> - cookieWithName("JSESSIONID").description("Session token")), // <3> - responseCookies(// <4> - cookieWithName("JSESSIONID").description("Updated session token"), - cookieWithName("logged_in").description("User is logged in")))); - // end::cookies[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/HttpHeaders.java b/docs/src/test/java/com/example/webtestclient/HttpHeaders.java deleted file mode 100644 index 9e0041851..000000000 --- a/docs/src/test/java/com/example/webtestclient/HttpHeaders.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; -import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class HttpHeaders { - - // @formatter:off - - private WebTestClient webTestClient; - - public void headers() { - // tag::headers[] - this.webTestClient - .get().uri("/people").header("Authorization", "Basic dXNlcjpzZWNyZXQ=") // <1> - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("headers", - requestHeaders(// <2> - headerWithName("Authorization").description("Basic auth credentials")), // <3> - responseHeaders(// <4> - headerWithName("X-RateLimit-Limit") - .description("The total number of requests permitted per period"), - headerWithName("X-RateLimit-Remaining") - .description("Remaining requests permitted in current period"), - headerWithName("X-RateLimit-Reset") - .description("Time at which the rate limit period will reset")))); - // end::headers[] - } -} diff --git a/docs/src/test/java/com/example/webtestclient/Hypermedia.java b/docs/src/test/java/com/example/webtestclient/Hypermedia.java deleted file mode 100644 index 93b79ed5a..000000000 --- a/docs/src/test/java/com/example/webtestclient/Hypermedia.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.http.MediaType; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class Hypermedia { - - private WebTestClient webTestClient; - - public void defaultExtractor() { - // tag::links[] - this.webTestClient.get() - .uri("/") - .accept(MediaType.APPLICATION_JSON) - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("index", links(// <1> - linkWithRel("alpha").description("Link to the alpha resource"), // <2> - linkWithRel("bravo").description("Link to the bravo resource")))); // <3> - // end::links[] - } - - public void explicitExtractor() { - this.webTestClient.get() - .uri("/") - .accept(MediaType.APPLICATION_JSON) - .exchange() - .expectStatus() - .isOk() - .expectBody() - // tag::explicit-extractor[] - .consumeWith(document("index", links(halLinks(), // <1> - linkWithRel("alpha").description("Link to the alpha resource"), - linkWithRel("bravo").description("Link to the bravo resource")))); - // end::explicit-extractor[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/InvokeService.java b/docs/src/test/java/com/example/webtestclient/InvokeService.java deleted file mode 100644 index 20859f27a..000000000 --- a/docs/src/test/java/com/example/webtestclient/InvokeService.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.http.MediaType; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class InvokeService { - - private WebTestClient webTestClient; - - public void invokeService() { - // tag::invoke-service[] - this.webTestClient.get() - .uri("/") - .accept(MediaType.APPLICATION_JSON) // <1> - .exchange() - .expectStatus() - .isOk() // <2> - .expectBody() - .consumeWith(document("index")); // <3> - // end::invoke-service[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java b/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java deleted file mode 100644 index f37702746..000000000 --- a/docs/src/test/java/com/example/webtestclient/ParameterizedOutput.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; - -@ExtendWith(RestDocumentationExtension.class) -class ParameterizedOutput { - - @SuppressWarnings("unused") - private WebTestClient webTestClient; - - @Autowired - private ApplicationContext context; - - // tag::parameterized-output[] - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.webTestClient = WebTestClient.bindToApplicationContext(this.context) - .configureClient() - .filter(documentationConfiguration(restDocumentation)) - .entityExchangeResultConsumer(document("{method-name}/{step}")) - .build(); - } - // end::parameterized-output[] - -} diff --git a/docs/src/test/java/com/example/webtestclient/PathParameters.java b/docs/src/test/java/com/example/webtestclient/PathParameters.java deleted file mode 100644 index 5199f6802..000000000 --- a/docs/src/test/java/com/example/webtestclient/PathParameters.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class PathParameters { - - // @formatter:off - - private WebTestClient webTestClient; - - public void pathParametersSnippet() { - // tag::path-parameters[] - this.webTestClient.get().uri("/locations/{latitude}/{longitude}", 51.5072, 0.1275) // <1> - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("locations", - pathParameters(// <2> - parameterWithName("latitude").description("The location's latitude"), // <3> - parameterWithName("longitude").description("The location's longitude")))); // <4> - // end::path-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/Payload.java b/docs/src/test/java/com/example/webtestclient/Payload.java deleted file mode 100644 index d7d828e57..000000000 --- a/docs/src/test/java/com/example/webtestclient/Payload.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.FieldDescriptor; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class Payload { - - // @formatter:off - - private WebTestClient webTestClient; - - public void response() { - // tag::response[] - this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("user", - responseFields(// <1> - fieldWithPath("contact.email").description("The user's email address"), // <2> - fieldWithPath("contact.name").description("The user's name")))); // <3> - // end::response[] - } - - public void subsection() { - // tag::subsection[] - this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("user", - responseFields( - subsectionWithPath("contact").description("The user's contact details")))); // <1> - // end::subsection[] - } - - public void explicitType() { - this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - // tag::explicit-type[] - .consumeWith(document("user", - responseFields( - fieldWithPath("contact.email") - .type(JsonFieldType.STRING) // <1> - .description("The user's email address")))); - // end::explicit-type[] - } - - public void constraints() { - this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - // tag::constraints[] - .consumeWith(document("create-user", - requestFields( - attributes(key("title").value("Fields for user creation")), // <1> - fieldWithPath("name") - .description("The user's name") - .attributes(key("constraints").value("Must not be null. Must not be empty")), // <2> - fieldWithPath("email") - .description("The user's email address") - .attributes(key("constraints").value("Must be a valid email address"))))); // <3> - // end::constraints[] - } - - public void descriptorReuse() { - FieldDescriptor[] book = new FieldDescriptor[] { - fieldWithPath("title").description("Title of the book"), - fieldWithPath("author").description("Author of the book") }; - - // tag::single-book[] - this.webTestClient.get().uri("/books/1").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("book", - responseFields(book))); // <1> - // end::single-book[] - - // tag::book-array[] - this.webTestClient.get().uri("/books").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("books", - responseFields( - fieldWithPath("[]") - .description("An array of books")) // <1> - .andWithPrefix("[].", book))); // <2> - // end::book-array[] - } - - public void fieldsSubsection() { - // tag::fields-subsection[] - this.webTestClient.get().uri("/locations/1").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("temperature", - responseFields(beneathPath("weather.temperature"), // <1> - fieldWithPath("high").description("The forecast high in degrees celcius"), // <2> - fieldWithPath("low").description("The forecast low in degrees celcius")))); - // end::fields-subsection[] - } - - public void bodySubsection() { - // tag::body-subsection[] - this.webTestClient.get().uri("/locations/1").accept(MediaType.APPLICATION_JSON) - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("temperature", - responseBody(beneathPath("weather.temperature")))); // <1> - // end::body-subsection[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/PerTestPreprocessing.java b/docs/src/test/java/com/example/webtestclient/PerTestPreprocessing.java deleted file mode 100644 index cd1254efe..000000000 --- a/docs/src/test/java/com/example/webtestclient/PerTestPreprocessing.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class PerTestPreprocessing { - - // @formatter:off - - private WebTestClient webTestClient; - - public void general() { - // tag::preprocessing[] - this.webTestClient.get().uri("/").exchange().expectStatus().isOk().expectBody() - .consumeWith(document("index", - preprocessRequest(modifyHeaders().remove("Foo")), // <1> - preprocessResponse(prettyPrint()))); // <2> - // end::preprocessing[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/QueryParameters.java b/docs/src/test/java/com/example/webtestclient/QueryParameters.java deleted file mode 100644 index 347c700b6..000000000 --- a/docs/src/test/java/com/example/webtestclient/QueryParameters.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class QueryParameters { - - // @formatter:off - - private WebTestClient webTestClient; - - public void getQueryStringSnippet() { - // tag::query-parameters[] - this.webTestClient.get().uri("/users?page=2&per_page=100") // <1> - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("users", queryParameters(// <2> - parameterWithName("page").description("The page to retrieve"), // <3> - parameterWithName("per_page").description("Entries per page") // <4> - ))); - // end::query-parameters[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/RequestPartPayload.java b/docs/src/test/java/com/example/webtestclient/RequestPartPayload.java deleted file mode 100644 index e88126271..000000000 --- a/docs/src/test/java/com/example/webtestclient/RequestPartPayload.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import java.util.Collections; - -import org.springframework.core.io.ByteArrayResource; -import org.springframework.core.io.Resource; -import org.springframework.http.MediaType; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserters; - -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class RequestPartPayload { - - // @formatter:off - - private WebTestClient webTestClient; - - public void fields() { - // tag::fields[] - MultiValueMap multipartData = new LinkedMultiValueMap<>(); - Resource imageResource = new ByteArrayResource("<>".getBytes()) { - - @Override - public String getFilename() { - return "image.png"; - } - - }; - multipartData.add("image", imageResource); - multipartData.add("metadata", Collections.singletonMap("version", "1.0")); - this.webTestClient.post().uri("/images").body(BodyInserters.fromMultipartData(multipartData)) - .accept(MediaType.APPLICATION_JSON).exchange() - .expectStatus().isOk().expectBody() - .consumeWith(document("image-upload", - requestPartFields("metadata", // <1> - fieldWithPath("version").description("The version of the image")))); // <2> - // end::fields[] - } - - public void body() { - // tag::body[] - MultiValueMap multipartData = new LinkedMultiValueMap<>(); - Resource imageResource = new ByteArrayResource("<>".getBytes()) { - - @Override - public String getFilename() { - return "image.png"; - } - - }; - multipartData.add("image", imageResource); - multipartData.add("metadata", Collections.singletonMap("version", "1.0")); - - this.webTestClient.post().uri("/images").body(BodyInserters.fromMultipartData(multipartData)) - .accept(MediaType.APPLICATION_JSON).exchange() - .expectStatus().isOk().expectBody() - .consumeWith(document("image-upload", - requestPartBody("metadata"))); // <1> - // end::body[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/RequestParts.java b/docs/src/test/java/com/example/webtestclient/RequestParts.java deleted file mode 100644 index 5d65f0910..000000000 --- a/docs/src/test/java/com/example/webtestclient/RequestParts.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyInserters; - -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.request.RequestDocumentation.requestParts; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class RequestParts { - - // @formatter:off - - private WebTestClient webTestClient; - - public void upload() { - // tag::request-parts[] - MultiValueMap multipartData = new LinkedMultiValueMap<>(); - multipartData.add("file", "example".getBytes()); - this.webTestClient.post().uri("/upload").body(BodyInserters.fromMultipartData(multipartData)) // <1> - .exchange().expectStatus().isOk().expectBody() - .consumeWith(document("upload", requestParts(// <2> - partWithName("file").description("The file to upload")) // <3> - )); - // end::request-parts[] - } - -} diff --git a/docs/src/test/java/com/example/webtestclient/WebTestClientSnippetReuse.java b/docs/src/test/java/com/example/webtestclient/WebTestClientSnippetReuse.java deleted file mode 100644 index 6f57fc91d..000000000 --- a/docs/src/test/java/com/example/webtestclient/WebTestClientSnippetReuse.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.webtestclient; - -import com.example.SnippetReuse; - -import org.springframework.http.MediaType; -import org.springframework.test.web.reactive.server.WebTestClient; - -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; - -public class WebTestClientSnippetReuse extends SnippetReuse { - - // @formatter:off - - private WebTestClient webTestClient; - - public void documentation() { - // tag::use[] - this.webTestClient.get().uri("/").accept(MediaType.APPLICATION_JSON).exchange() - .expectStatus().isOk().expectBody() - .consumeWith(document("example", this.pagingLinks.and(// <1> - linkWithRel("alpha").description("Link to the alpha resource"), - linkWithRel("bravo").description("Link to the bravo resource")))); - // end::use[] - } - -} diff --git a/git/hooks/forward-merge b/git/hooks/forward-merge deleted file mode 100755 index a042bb460..000000000 --- a/git/hooks/forward-merge +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/ruby -require 'json' -require 'net/http' -require 'yaml' -require 'logger' - -$log = Logger.new(STDOUT) -$log.level = Logger::WARN - -class ForwardMerge - attr_reader :issue, :milestone, :message, :line - def initialize(issue, milestone, message, line) - @issue = issue - @milestone = milestone - @message = message - @line = line - end -end - -def find_forward_merges(message_file) - - $log.debug "Searching for forward merge" - branch=`git rev-parse -q --abbrev-ref HEAD`.strip - $log.debug "Found #{branch} from git rev-parse --abbrev-ref" - if( branch == "docs-build") then - $log.debug "Skipping docs build" - return nil - end - rev=`git rev-parse -q --verify MERGE_HEAD`.strip - $log.debug "Found #{rev} from git rev-parse" - return nil unless rev - message = File.read(message_file) - forward_merges = [] - message.each_line do |line| - $log.debug "Checking #{line} for message" - match = /^(?:Fixes|Closes) gh-(\d+) in (\d\.\d\.[\dx](?:[\.\-](?:M|RC)\d)?)$/.match(line) - if match then - issue = match[1] - milestone = match[2] - $log.debug "Matched reference to issue #{issue} in milestone #{milestone}" - forward_merges << ForwardMerge.new(issue, milestone, message, line) - end - end - $log.debug "No match in merge message" unless forward_merges - return forward_merges -end - -def get_issue(username, password, repository, number) - $log.debug "Getting issue #{number} from GitHub repository #{repository}" - uri = URI("https://api.github.com/repos/#{repository}/issues/#{number}") - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl=true - request = Net::HTTP::Get.new(uri.path) - request.basic_auth(username, password) - response = http.request(request) - $log.debug "Get HTTP response #{response.code}" - return JSON.parse(response.body) unless response.code != '200' - puts "Failed to retrieve issue #{number}: #{response.message}" - exit 1 -end - -def find_milestone(username, password, repository, title) - $log.debug "Finding milestone #{title} from GitHub repository #{repository}" - uri = URI("https://api.github.com/repos/#{repository}/milestones") - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl=true - request = Net::HTTP::Get.new(uri.path) - request.basic_auth(username, password) - response = http.request(request) - milestones = JSON.parse(response.body) - if title.end_with?(".x") - prefix = title.delete_suffix('.x') - $log.debug "Finding nearest milestone from candidates starting with #{prefix}" - titles = milestones.map { |milestone| milestone['title'] } - titles = titles.select{ |title| title.start_with?(prefix) unless title.end_with?('.x') || (title.count('.') > 2)} - titles = titles.sort_by { |v| Gem::Version.new(v) } - $log.debug "Considering candidates #{titles}" - if(titles.empty?) - puts "Cannot find nearest milestone for prefix #{title}" - exit 1 - end - title = titles.first - $log.debug "Found nearest milestone #{title}" - end - milestones.each do |milestone| - $log.debug "Considering #{milestone['title']}" - return milestone['number'] if milestone['title'] == title - end - puts "Milestone #{title} not found" - exit 1 -end - -def create_issue(username, password, repository, original, title, labels, milestone, milestone_name, dry_run) - $log.debug "Finding forward-merge issue in GitHub repository #{repository} for '#{title}'" - uri = URI("https://api.github.com/repos/#{repository}/issues") - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl=true - request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json') - request.basic_auth(username, password) - request.body = { - title: title, - labels: labels, - milestone: milestone.to_i, - body: "Forward port of issue ##{original} to #{milestone_name}." - }.to_json - if dry_run then - puts "Dry run" - puts "POSTing to #{uri} with body #{request.body}" - return "dry-run" - end - response = JSON.parse(http.request(request).body) - $log.debug "Created new issue #{response['number']}" - return response['number'] -end - -$log.debug "Running forward-merge hook script" -message_file=ARGV[0] - -forward_merges = find_forward_merges(message_file) -exit 0 unless forward_merges - -$log.debug "Loading config from ~/.spring-restdocs/forward-merge.yml" -config = YAML.load_file(File.join(Dir.home, '.spring-restdocs', 'forward-merge.yml')) -username = config['github']['credentials']['username'] -password = config['github']['credentials']['password'] -dry_run = config['dry_run'] - -gradleProperties = IO.read('gradle.properties') -springBuildType = gradleProperties.match(/^spring\.build-type\s?=\s?(.*)$/) -repository = (springBuildType && springBuildType[1] != 'oss') ? "spring-projects/spring-restdocs-#{springBuildType[1]}" : "spring-projects/spring-restdocs"; -$log.debug "Targeting repository #{repository}" - -forward_merges.each do |forward_merge| - existing_issue = get_issue(username, password, repository, forward_merge.issue) - title = existing_issue['title'] - labels = existing_issue['labels'].map { |label| label['name'] } - labels << "status: forward-port" - $log.debug "Processing issue '#{title}'" - - milestone = find_milestone(username, password, repository, forward_merge.milestone) - new_issue_number = create_issue(username, password, repository, forward_merge.issue, title, labels, milestone, forward_merge.milestone, dry_run) - - puts "Created gh-#{new_issue_number} for forward port of gh-#{forward_merge.issue} into #{forward_merge.milestone}" - rewritten_message = forward_merge.message.sub(forward_merge.line, "Closes gh-#{new_issue_number}\n") - File.write(message_file, rewritten_message) -end diff --git a/git/hooks/prepare-forward-merge b/git/hooks/prepare-forward-merge deleted file mode 100755 index fbdb1e194..000000000 --- a/git/hooks/prepare-forward-merge +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/ruby -require 'json' -require 'net/http' -require 'yaml' -require 'logger' - -$main_branch = "4.0.x" - -$log = Logger.new(STDOUT) -$log.level = Logger::WARN - -def get_fixed_issues() - $log.debug "Searching for forward merge" - rev=`git rev-parse -q --verify MERGE_HEAD`.strip - $log.debug "Found #{rev} from git rev-parse" - return nil unless rev - fixed = [] - message = `git log -1 --pretty=%B #{rev}` - message.each_line do |line| - $log.debug "Checking #{line} for message" - fixed << line.strip if /^(?:Fixes|Closes) gh-(\d+)/.match(line) - end - $log.debug "Found fixed issues #{fixed}" - return fixed; -end - -def rewrite_message(message_file, fixed) - current_branch = `git rev-parse --abbrev-ref HEAD`.strip - if current_branch == "main" - current_branch = $main_branch - end - rewritten_message = "" - message = File.read(message_file) - message.each_line do |line| - match = /^Merge.*branch\ '(.*)'(?:\ into\ (.*))?$/.match(line) - if match - from_branch = match[1] - if from_branch.include? "/" - from_branch = from_branch.partition("/").last - end - to_branch = match[2] - $log.debug "Rewriting merge message" - line = "Merge branch '#{from_branch}'" + (to_branch ? " into #{to_branch}\n" : "\n") - end - if fixed and line.start_with?("#") - $log.debug "Adding fixed" - rewritten_message << "\n" - fixed.each do |fixes| - rewritten_message << "#{fixes} in #{current_branch}\n" - end - fixed = nil - end - rewritten_message << line - end - return rewritten_message -end - -$log.debug "Running prepare-forward-merge hook script" - -message_file=ARGV[0] -message_type=ARGV[1] - -if message_type != "merge" - $log.debug "Not a merge commit" - exit 0; -end - -$log.debug "Searching for forward merge" -fixed = get_fixed_issues() -rewritten_message = rewrite_message(message_file, fixed) -File.write(message_file, rewritten_message) diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 5cc794136..000000000 --- a/gradle.properties +++ /dev/null @@ -1,9 +0,0 @@ -version=4.0.0-SNAPSHOT - -org.gradle.caching=true -org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 -org.gradle.parallel=true - -javaFormatVersion=0.0.43 -jmustacheVersion=1.15 -springFrameworkVersion=7.0.0-M1 diff --git a/gradle/publish-maven.gradle b/gradle/publish-maven.gradle deleted file mode 100644 index 3c50ccb7f..000000000 --- a/gradle/publish-maven.gradle +++ /dev/null @@ -1,62 +0,0 @@ -plugins.withType(MavenPublishPlugin) { - publishing { - if (project.hasProperty("deploymentRepository")) { - repositories { - maven { - url = project.property("deploymentRepository") - name = "deployment" - } - } - } - publications { - maven(MavenPublication) { publication -> - project.plugins.withType(JavaPlugin) { - from components.java - versionMapping { - usage("java-api") { - fromResolutionResult() - } - usage("java-runtime") { - fromResolutionResult() - } - } - } - project.plugins.withType(JavaPlatformPlugin) { - from components.javaPlatform - } - pom { - name = project.provider { project.description } - description = project.provider { project.description } - url = "https://github.com/spring-projects/spring-restdocs" - organization { - name = "Spring IO" - url = "https://projects.spring.io/spring-restdocs" - } - licenses { - license { - name = "The Apache Software License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "repo" - } - } - scm { - url = "https://github.com/spring-projects/spring-restdocs" - connection = "scm:git:git://github.com/spring-projects/spring-restdocs" - developerConnection = "scm:git:git://github.com/spring-projects/spring-restdocs" - } - developers { - developer { - id = "awilkinson" - name = "Andy Wilkinson" - email = "awilkinson@pivotal.io" - } - } - issueManagement { - system = "GitHub" - url = "https://github.com/spring-projects/spring-restdocs/issues" - } - } - } - } - } -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 1b33c55ba..000000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 002b867c4..000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 23d15a936..000000000 --- a/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index db3a6ac20..000000000 --- a/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/index.html b/index.html new file mode 100644 index 000000000..7cfa3540f --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + +

Redirecting…

+ Click here if you are not redirected. + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index fd285ac1a..000000000 --- a/settings.gradle +++ /dev/null @@ -1,29 +0,0 @@ -pluginManagement { - repositories { - mavenCentral() - gradlePluginPortal() - maven { url = 'https://repo.spring.io/snapshot' } - } - resolutionStrategy { - eachPlugin { - if (requested.id.id == "io.spring.javaformat") { - useModule "io.spring.javaformat:spring-javaformat-gradle-plugin:${requested.version}" - } - } - } -} - -plugins { - id "io.spring.develocity.conventions" version "0.0.23" -} - -rootProject.name = "spring-restdocs" - -include "docs" -include "spring-restdocs-asciidoctor" -include "spring-restdocs-bom" -include "spring-restdocs-core" -include "spring-restdocs-mockmvc" -include "spring-restdocs-platform" -include "spring-restdocs-restassured" -include "spring-restdocs-webtestclient" diff --git a/spring-restdocs-asciidoctor/build.gradle b/spring-restdocs-asciidoctor/build.gradle deleted file mode 100644 index f9d1f685d..000000000 --- a/spring-restdocs-asciidoctor/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -plugins { - id "java-library" - id "maven-publish" -} - -description = "Spring REST Docs Asciidoctor Extension" - -dependencies { - implementation("org.asciidoctor:asciidoctorj") - - internal(platform(project(":spring-restdocs-platform"))) - - testImplementation("org.apache.pdfbox:pdfbox") - testImplementation("org.assertj:assertj-core") - testImplementation("org.junit.jupiter:junit-jupiter") - testImplementation("org.springframework:spring-core") - - testRuntimeOnly("org.asciidoctor:asciidoctorj-pdf") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -tasks.named("test") { - useJUnitPlatform() -} diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java deleted file mode 100644 index 7fa6ce5df..000000000 --- a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import org.asciidoctor.ast.Document; -import org.asciidoctor.extension.Preprocessor; -import org.asciidoctor.extension.PreprocessorReader; -import org.asciidoctor.extension.Reader; - -/** - * {@link Preprocessor} that sets defaults for REST Docs-related {@link Document} - * attributes. - * - * @author Andy Wilkinson - */ -final class DefaultAttributesPreprocessor extends Preprocessor { - - private final SnippetsDirectoryResolver snippetsDirectoryResolver = new SnippetsDirectoryResolver(); - - @Override - public Reader process(Document document, PreprocessorReader reader) { - document.setAttribute("snippets", this.snippetsDirectoryResolver.getSnippetsDirectory(document.getAttributes()), - false); - return reader; - } - -} diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java deleted file mode 100644 index 0a58a7cf9..000000000 --- a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/RestDocsExtensionRegistry.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.jruby.extension.spi.ExtensionRegistry; - -/** - * {@link ExtensionRegistry} for Spring REST Docs. - * - * @author Andy Wilkinson - */ -public final class RestDocsExtensionRegistry implements ExtensionRegistry { - - @Override - public void register(Asciidoctor asciidoctor) { - asciidoctor.javaExtensionRegistry().preprocessor(new DefaultAttributesPreprocessor()); - asciidoctor.rubyExtensionRegistry() - .loadClass(RestDocsExtensionRegistry.class.getResourceAsStream("/extensions/operation_block_macro.rb")) - .blockMacro("operation", "OperationBlockMacro"); - } - -} diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java deleted file mode 100644 index dfbb957a8..000000000 --- a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolver.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2014-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; -import java.util.function.Supplier; - -/** - * Resolves the directory from which snippets can be read for inclusion in an Asciidoctor - * document. The resolved directory is absolute. - * - * @author Andy Wilkinson - */ -class SnippetsDirectoryResolver { - - File getSnippetsDirectory(Map attributes) { - if (System.getProperty("maven.home") != null) { - return getMavenSnippetsDirectory(attributes); - } - return getGradleSnippetsDirectory(attributes); - } - - private File getMavenSnippetsDirectory(Map attributes) { - Path docdir = Paths.get(getRequiredAttribute(attributes, "docdir")); - return new File(findPom(docdir).getParent().toFile(), "target/generated-snippets").getAbsoluteFile(); - } - - private Path findPom(Path docdir) { - Path path = docdir; - while (path != null) { - Path pom = path.resolve("pom.xml"); - if (Files.isRegularFile(pom)) { - return pom; - } - path = path.getParent(); - } - throw new IllegalStateException("pom.xml not found in '" + docdir + "' or above"); - } - - private File getGradleSnippetsDirectory(Map attributes) { - return new File(getRequiredAttribute(attributes, "gradle-projectdir", - () -> getRequiredAttribute(attributes, "projectdir")), "build/generated-snippets") - .getAbsoluteFile(); - } - - private String getRequiredAttribute(Map attributes, String name) { - return getRequiredAttribute(attributes, name, null); - } - - private String getRequiredAttribute(Map attributes, String name, Supplier fallback) { - String attribute = (String) attributes.get(name); - if (attribute == null || attribute.length() == 0) { - if (fallback != null) { - return fallback.get(); - } - throw new IllegalStateException(name + " attribute not found"); - } - return attribute; - } - -} diff --git a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/package-info.java b/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/package-info.java deleted file mode 100644 index fd03600b7..000000000 --- a/spring-restdocs-asciidoctor/src/main/java/org/springframework/restdocs/asciidoctor/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Support for Asciidoctor. - */ -package org.springframework.restdocs.asciidoctor; diff --git a/spring-restdocs-asciidoctor/src/main/resources/META-INF/services/org.asciidoctor.jruby.extension.spi.ExtensionRegistry b/spring-restdocs-asciidoctor/src/main/resources/META-INF/services/org.asciidoctor.jruby.extension.spi.ExtensionRegistry deleted file mode 100644 index 7123d5b51..000000000 --- a/spring-restdocs-asciidoctor/src/main/resources/META-INF/services/org.asciidoctor.jruby.extension.spi.ExtensionRegistry +++ /dev/null @@ -1 +0,0 @@ -org.springframework.restdocs.asciidoctor.RestDocsExtensionRegistry diff --git a/spring-restdocs-asciidoctor/src/main/resources/extensions/operation_block_macro.rb b/spring-restdocs-asciidoctor/src/main/resources/extensions/operation_block_macro.rb deleted file mode 100644 index 66feb5f5e..000000000 --- a/spring-restdocs-asciidoctor/src/main/resources/extensions/operation_block_macro.rb +++ /dev/null @@ -1,156 +0,0 @@ -require 'asciidoctor/extensions' -require 'stringio' -require 'asciidoctor/logging' - -# Spring REST Docs block macro to import multiple snippet of an operation at -# once -# -# Usage -# -# operation::operation-name[snippets='snippet-name1,snippet-name2'] -# -class OperationBlockMacro < Asciidoctor::Extensions::BlockMacroProcessor - use_dsl - named :operation - include Asciidoctor::Logging - - def process(parent, operation, attributes) - snippets_dir = parent.document.attributes['snippets'].to_s - snippet_names = attributes.fetch 'snippets', '' - operation = parent.sub_attributes operation - snippet_titles = SnippetTitles.new parent.document.attributes - content = read_snippets(snippets_dir, snippet_names, parent, operation, - snippet_titles) - add_blocks(content, parent.document, parent) unless content.empty? - nil - end - - def read_snippets(snippets_dir, snippet_names, parent, operation, - snippet_titles) - snippets = snippets_to_include(snippet_names, snippets_dir, operation) - if snippets.empty? - location = parent.document.reader.cursor_at_mark - logger.warn message_with_context "No snippets were found for operation #{operation} in "\ - "#{snippets_dir}", source_location: location - "No snippets found for operation::#{operation}" - else - do_read_snippets(snippets, parent, operation, snippet_titles) - end - end - - def do_read_snippets(snippets, parent, operation, snippet_titles) - content = StringIO.new - content.set_encoding "UTF-8" - section_id = parent.id - snippets.each do |snippet| - append_snippet_block(content, snippet, section_id, - operation, snippet_titles, parent) - end - content.string - end - - def add_blocks(content, doc, parent) - options = { safe: doc.options[:safe], attributes: doc.attributes.clone } - options[:attributes].delete 'leveloffset' - fragment = Asciidoctor.load content, options - # use a template to get the correct sectname and level for blocks to append - template = create_section(parent, '', {}) - fragment.blocks.each do |b| - b.parent = parent - # might be a standard block and no section in case of 'No snippets were found for operation' - if b.respond_to?(:sectname) - b.sectname = template.sectname - end - b.level = template.level - parent << b - end - parent.find_by.each do |b| - b.parent = b.parent unless b.is_a? Asciidoctor::Document - end - end - - def snippets_to_include(snippet_names, snippets_dir, operation) - if snippet_names.empty? - all_snippets snippets_dir, operation - else - snippet_names.split(',').map do |name| - path = File.join snippets_dir, operation, "#{name}.adoc" - Snippet.new path, name - end - end - end - - def all_snippets(snippets_dir, operation) - operation_dir = File.join snippets_dir, operation - return [] unless Dir.exist? operation_dir - Dir.entries(operation_dir) - .sort - .select { |file| file.end_with? '.adoc' } - .map { |file| Snippet.new(File.join(operation_dir, file), file[0..-6]) } - end - - def append_snippet_block(content, snippet, section_id, - operation, snippet_titles, parent) - write_title content, snippet, section_id, snippet_titles - write_content content, snippet, operation, parent - end - - def write_content(content, snippet, operation, parent) - if File.file? snippet.path - content.puts File.readlines(snippet.path, :encoding => 'UTF-8').join - else - location = parent.document.reader.cursor_at_mark - logger.warn message_with_context "Snippet #{snippet.name} not found at #{snippet.path} for"\ - " operation #{operation}", source_location: location - content.puts "Snippet #{snippet.name} not found for"\ - " operation::#{operation}" - content.puts '' - end - end - - def write_title(content, snippet, id, snippet_titles) - section_level = '==' - title = snippet_titles.title_for_snippet snippet - content.puts "[[#{id}_#{snippet.name.sub '-', '_'}]]" - content.puts "#{section_level} #{title}" - content.puts '' - end - - # Details of a snippet to be rendered - class Snippet - attr_reader :name, :path - - def initialize(path, name) - @path = path - @name = name - @snippet_titles - end - end - - class SnippetTitles - @defaults = { 'http-request' => 'HTTP request', - 'curl-request' => 'Curl request', - 'httpie-request' => 'HTTPie request', - 'request-body' => 'Request body', - 'request-fields' => 'Request fields', - 'http-response' => 'HTTP response', - 'response-body' => 'Response body', - 'response-fields' => 'Response fields', - 'links' => 'Links' } - - class << self - attr_reader :defaults - end - - def initialize(document_attributes) - @document_attributes = document_attributes - end - - def title_for_snippet(snippet) - attribute_name = "operation-#{snippet.name}-title" - @document_attributes.fetch attribute_name do - SnippetTitles.defaults.fetch snippet.name, snippet.name.sub('-', ' ').capitalize - end - end - end -end diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java deleted file mode 100644 index d6df5f917..000000000 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/AbstractOperationBlockMacroTests.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; - -import org.apache.pdfbox.contentstream.PDFStreamEngine; -import org.apache.pdfbox.contentstream.operator.Operator; -import org.apache.pdfbox.cos.COSBase; -import org.apache.pdfbox.cos.COSString; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.Attributes; -import org.asciidoctor.Options; -import org.asciidoctor.SafeMode; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import org.springframework.util.FileSystemUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Base class for tests for the Ruby operation block macro. - * - * @author Gerrit Meier - * @author Andy Wilkinson - */ -abstract class AbstractOperationBlockMacroTests { - - private final Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - - @TempDir - protected File temp; - - private Options options; - - @BeforeEach - void setUp() throws IOException { - prepareOperationSnippets(getBuildOutputLocation()); - this.options = Options.builder().safe(SafeMode.UNSAFE).baseDir(getSourceLocation()).build(); - this.options.setAttributes(getAttributes()); - CapturingLogHandler.clear(); - } - - @AfterEach - void verifyLogging() { - assertThat(CapturingLogHandler.getLogRecords()).isEmpty(); - } - - private void prepareOperationSnippets(File buildOutputLocation) throws IOException { - File destination = new File(buildOutputLocation, "generated-snippets/some-operation"); - destination.mkdirs(); - FileSystemUtils.copyRecursively(new File("src/test/resources/some-operation"), destination); - } - - @Test - void codeBlockSnippetInclude() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[snippets='curl-request']", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-simple")); - } - - @Test - void operationWithParameterizedName() throws Exception { - Attributes attributes = getAttributes(); - attributes.setAttribute("name", "some"); - this.options.setAttributes(attributes); - String result = this.asciidoctor.convert("operation::{name}-operation[snippets='curl-request']", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-simple")); - } - - @Test - void codeBlockSnippetIncludeWithPdfBackend() throws Exception { - File output = configurePdfOutput(); - this.asciidoctor.convert("operation::some-operation[snippets='curl-request']", this.options); - assertThat(extractStrings(output)).containsExactly("Curl request", "$ curl 'http://localhost:8080/' -i", "1"); - } - - @Test - void tableSnippetInclude() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[snippets='response-fields']", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-table")); - } - - @Test - void tableSnippetIncludeWithPdfBackend() throws Exception { - File output = configurePdfOutput(); - this.asciidoctor.convert("operation::some-operation[snippets='response-fields']", this.options); - assertThat(extractStrings(output)).containsExactly("Response fields", "Path", "Type", "Description", "a", - "Object", "one", "a.b", "Number", "two", "a.c", "String", "three", "1"); - } - - @Test - void includeSnippetInSection() throws Exception { - String result = this.asciidoctor.convert("= A\n:doctype: book\n:sectnums:\n\nAlpha\n\n== B\n\nBravo\n\n" - + "operation::some-operation[snippets='curl-request']\n\n== C\n", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-in-section")); - } - - @Test - void includeSnippetInSectionWithAbsoluteLevelOffset() throws Exception { - String result = this.asciidoctor - .convert("= A\n:doctype: book\n:sectnums:\n:leveloffset: 1\n\nAlpha\n\n= B\n\nBravo\n\n" - + "operation::some-operation[snippets='curl-request']\n\n= C\n", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-in-section")); - } - - @Test - void includeSnippetInSectionWithRelativeLevelOffset() throws Exception { - String result = this.asciidoctor - .convert("= A\n:doctype: book\n:sectnums:\n:leveloffset: +1\n\nAlpha\n\n= B\n\nBravo\n\n" - + "operation::some-operation[snippets='curl-request']\n\n= C\n", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("snippet-in-section")); - } - - @Test - void includeSnippetInSectionWithPdfBackend() throws Exception { - File output = configurePdfOutput(); - this.asciidoctor.convert("== Section\n" + "operation::some-operation[snippets='curl-request']", this.options); - assertThat(extractStrings(output)).containsExactly("Section", "Curl request", - "$ curl 'http://localhost:8080/' -i", "1"); - } - - @Test - void includeMultipleSnippets() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[snippets='curl-request,http-request']", - this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("multiple-snippets")); - } - - @Test - void useMacroWithoutSnippetAttributeAddsAllSnippets() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[]", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("all-snippets")); - } - - @Test - void useMacroWithEmptySnippetAttributeAddsAllSnippets() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[snippets=]", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("all-snippets")); - } - - @Test - void includingMissingSnippetAddsWarning() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[snippets='missing-snippet']", this.options); - assertThat(result).startsWith(getExpectedContentFromFile("missing-snippet")); - assertThat(CapturingLogHandler.getLogRecords()).hasSize(1); - assertThat(CapturingLogHandler.getLogRecords().get(0).getMessage()) - .contains("Snippet missing-snippet not found"); - assertThat(CapturingLogHandler.getLogRecords().get(0).getCursor().getLineNumber()).isEqualTo(1); - CapturingLogHandler.getLogRecords().clear(); - } - - @Test - void defaultTitleIsProvidedForCustomSnippet() throws Exception { - String result = this.asciidoctor.convert("operation::some-operation[snippets='custom-snippet']", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("custom-snippet-default-title")); - } - - @Test - void missingOperationIsHandledGracefully() throws Exception { - String result = this.asciidoctor.convert("operation::missing-operation[]", this.options); - assertThat(result).startsWith(getExpectedContentFromFile("missing-operation")); - assertThat(CapturingLogHandler.getLogRecords()).hasSize(1); - assertThat(CapturingLogHandler.getLogRecords().get(0).getMessage()) - .contains("No snippets were found for operation missing-operation"); - assertThat(CapturingLogHandler.getLogRecords().get(0).getCursor().getLineNumber()).isEqualTo(1); - CapturingLogHandler.getLogRecords().clear(); - } - - @Test - void titleOfBuiltInSnippetCanBeCustomizedUsingDocumentAttribute() throws URISyntaxException, IOException { - String result = this.asciidoctor.convert(":operation-curl-request-title: Example request\n" - + "operation::some-operation[snippets='curl-request']", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("built-in-snippet-custom-title")); - } - - @Test - void titleOfCustomSnippetCanBeCustomizedUsingDocumentAttribute() throws Exception { - String result = this.asciidoctor.convert(":operation-custom-snippet-title: Customized title\n" - + "operation::some-operation[snippets='custom-snippet']", this.options); - assertThat(result).isEqualTo(getExpectedContentFromFile("custom-snippet-custom-title")); - } - - private String getExpectedContentFromFile(String fileName) throws URISyntaxException, IOException { - Path filePath = Paths.get(this.getClass().getResource("/operations/" + fileName + ".html").toURI()); - String content = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8); - if (isWindows()) { - return content.replace("\r\n", "\n"); - } - return content; - } - - private boolean isWindows() { - return File.separatorChar == '\\'; - } - - protected abstract Attributes getAttributes(); - - protected abstract File getBuildOutputLocation(); - - protected abstract File getSourceLocation(); - - private File configurePdfOutput() { - this.options.setBackend("pdf"); - File output = new File("build/output.pdf"); - this.options.setToFile(output.getAbsolutePath()); - return output; - } - - private List extractStrings(File pdfFile) throws IOException { - PDDocument pdf = PDDocument.load(pdfFile); - assertThat(pdf.getNumberOfPages()).isEqualTo(1); - StringExtractor stringExtractor = new StringExtractor(); - stringExtractor.processPage(pdf.getPage(0)); - return stringExtractor.getStrings(); - } - - private static final class StringExtractor extends PDFStreamEngine { - - private final List strings = new ArrayList<>(); - - @Override - protected void processOperator(Operator operator, List operands) throws IOException { - if ("Tj".equals(operator.getName())) { - for (COSBase operand : operands) { - if (operand instanceof COSString) { - this.strings.add((((COSString) operand).getASCII())); - } - } - } - } - - private List getStrings() { - return this.strings; - } - - } - -} diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/CapturingLogHandler.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/CapturingLogHandler.java deleted file mode 100644 index f60ce09c4..000000000 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/CapturingLogHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.util.ArrayList; -import java.util.List; - -import org.asciidoctor.log.LogHandler; -import org.asciidoctor.log.LogRecord; - -public class CapturingLogHandler implements LogHandler { - - private static final List logRecords = new ArrayList(); - - @Override - public void log(LogRecord logRecord) { - logRecords.add(logRecord); - } - - static List getLogRecords() { - return logRecords; - } - - static void clear() { - logRecords.clear(); - } - -} diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java deleted file mode 100644 index b5921dbaa..000000000 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/DefaultAttributesPreprocessorTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.io.File; - -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.Attributes; -import org.asciidoctor.Options; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link DefaultAttributesPreprocessor}. - * - * @author Andy Wilkinson - */ -class DefaultAttributesPreprocessorTests { - - @Test - void snippetsAttributeIsSet() { - String converted = createAsciidoctor().convert("{snippets}", createOptions("projectdir=../../..")); - assertThat(converted).contains("build" + File.separatorChar + "generated-snippets"); - } - - @Test - void snippetsAttributeFromConvertArgumentIsNotOverridden() { - String converted = createAsciidoctor().convert("{snippets}", - createOptions("snippets=custom projectdir=../../..")); - assertThat(converted).contains("custom"); - } - - @Test - void snippetsAttributeFromDocumentPreambleIsNotOverridden() { - String converted = createAsciidoctor().convert(":snippets: custom\n{snippets}", - createOptions("projectdir=../../..")); - assertThat(converted).contains("custom"); - } - - private Options createOptions(String attributes) { - Options options = Options.builder().build(); - options.setAttributes(Attributes.builder().arguments(attributes).build()); - return options; - } - - private Asciidoctor createAsciidoctor() { - Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - asciidoctor.javaExtensionRegistry().preprocessor(new DefaultAttributesPreprocessor()); - return asciidoctor; - } - -} diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java deleted file mode 100644 index 1ed21adb9..000000000 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/GradleOperationBlockMacroTests.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.io.File; - -import org.asciidoctor.Attributes; -import org.junit.jupiter.params.ParameterizedClass; -import org.junit.jupiter.params.provider.ValueSource; - -/** - * Tests for Ruby operation block macro when used in a Gradle build. - * - * @author Andy Wilkinson - */ -@ParameterizedClass -@ValueSource(strings = { "projectdir", "gradle-projectdir" }) -class GradleOperationBlockMacroTests extends AbstractOperationBlockMacroTests { - - private final String attributeName; - - GradleOperationBlockMacroTests(String attributeName) { - this.attributeName = attributeName; - } - - @Override - protected Attributes getAttributes() { - Attributes attributes = Attributes.builder() - .attribute(this.attributeName, new File(this.temp, "gradle-project").getAbsolutePath()) - .build(); - return attributes; - } - - @Override - protected File getBuildOutputLocation() { - File outputLocation = new File(this.temp, "gradle-project/build"); - outputLocation.mkdirs(); - return outputLocation; - } - - @Override - protected File getSourceLocation() { - File sourceLocation = new File(this.temp, "gradle-project/src/docs/asciidoc"); - if (!sourceLocation.exists()) { - sourceLocation.mkdirs(); - } - return sourceLocation; - } - -} diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java deleted file mode 100644 index f02f326f9..000000000 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/MavenOperationBlockMacroTests.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.io.File; -import java.io.IOException; - -import org.asciidoctor.Attributes; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; - -/** - * Tests for Ruby operation block macro when used in a Maven build. - * - * @author Andy Wilkinson - */ -class MavenOperationBlockMacroTests extends AbstractOperationBlockMacroTests { - - @BeforeEach - void setMavenHome() { - System.setProperty("maven.home", "maven-home"); - } - - @AfterEach - void clearMavenHome() { - System.clearProperty("maven.home"); - } - - @Override - protected Attributes getAttributes() { - try { - File sourceLocation = getSourceLocation(); - new File(sourceLocation.getParentFile().getParentFile().getParentFile(), "pom.xml").createNewFile(); - Attributes attributes = Attributes.builder().attribute("docdir", sourceLocation.getAbsolutePath()).build(); - return attributes; - } - catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - @Override - protected File getBuildOutputLocation() { - File outputLocation = new File(this.temp, "maven-project/target"); - outputLocation.mkdirs(); - return outputLocation; - } - - @Override - protected File getSourceLocation() { - File sourceLocation = new File(this.temp, "maven-project/src/main/asciidoc"); - if (!sourceLocation.exists()) { - sourceLocation.mkdirs(); - } - return sourceLocation; - } - -} diff --git a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java b/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java deleted file mode 100644 index 9ca213b93..000000000 --- a/spring-restdocs-asciidoctor/src/test/java/org/springframework/restdocs/asciidoctor/SnippetsDirectoryResolverTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.asciidoctor; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; - -/** - * Tests for {@link SnippetsDirectoryResolver}. - * - * @author Andy Wilkinson - */ -public class SnippetsDirectoryResolverTests { - - @TempDir - File temp; - - @Test - public void mavenProjectsUseTargetGeneratedSnippets() throws IOException { - new File(this.temp, "pom.xml").createNewFile(); - Map attributes = new HashMap<>(); - attributes.put("docdir", new File(this.temp, "src/main/asciidoc").getAbsolutePath()); - File snippetsDirectory = getMavenSnippetsDirectory(attributes); - assertThat(snippetsDirectory).isAbsolute(); - assertThat(snippetsDirectory).isEqualTo(new File(this.temp, "target/generated-snippets")); - } - - @Test - public void illegalStateExceptionWhenMavenPomCannotBeFound() { - Map attributes = new HashMap<>(); - String docdir = new File(this.temp, "src/main/asciidoc").getAbsolutePath(); - attributes.put("docdir", docdir); - assertThatIllegalStateException().isThrownBy(() -> getMavenSnippetsDirectory(attributes)) - .withMessage("pom.xml not found in '" + docdir + "' or above"); - } - - @Test - public void illegalStateWhenDocdirAttributeIsNotSetInMavenProject() { - Map attributes = new HashMap<>(); - assertThatIllegalStateException().isThrownBy(() -> getMavenSnippetsDirectory(attributes)) - .withMessage("docdir attribute not found"); - } - - @Test - public void gradleProjectsUseBuildGeneratedSnippetsBeneathGradleProjectdir() { - Map attributes = new HashMap<>(); - attributes.put("gradle-projectdir", "project/dir"); - File snippetsDirectory = new SnippetsDirectoryResolver().getSnippetsDirectory(attributes); - assertThat(snippetsDirectory).isAbsolute(); - assertThat(snippetsDirectory).isEqualTo(new File("project/dir/build/generated-snippets").getAbsoluteFile()); - } - - @Test - public void gradleProjectsUseBuildGeneratedSnippetsBeneathGradleProjectdirWhenBothItAndProjectdirAreSet() { - Map attributes = new HashMap<>(); - attributes.put("gradle-projectdir", "project/dir"); - attributes.put("projectdir", "fallback/dir"); - File snippetsDirectory = new SnippetsDirectoryResolver().getSnippetsDirectory(attributes); - assertThat(snippetsDirectory).isAbsolute(); - assertThat(snippetsDirectory).isEqualTo(new File("project/dir/build/generated-snippets").getAbsoluteFile()); - } - - @Test - public void gradleProjectsUseBuildGeneratedSnippetsBeneathProjectdirWhenGradleProjectdirIsNotSet() { - Map attributes = new HashMap<>(); - attributes.put("projectdir", "project/dir"); - File snippetsDirectory = new SnippetsDirectoryResolver().getSnippetsDirectory(attributes); - assertThat(snippetsDirectory).isAbsolute(); - assertThat(snippetsDirectory).isEqualTo(new File("project/dir/build/generated-snippets").getAbsoluteFile()); - } - - @Test - public void illegalStateWhenGradleProjectdirAndProjectdirAttributesAreNotSetInGradleProject() { - Map attributes = new HashMap<>(); - assertThatIllegalStateException() - .isThrownBy(() -> new SnippetsDirectoryResolver().getSnippetsDirectory(attributes)) - .withMessage("projectdir attribute not found"); - } - - private File getMavenSnippetsDirectory(Map attributes) { - System.setProperty("maven.home", "/maven/home"); - try { - return new SnippetsDirectoryResolver().getSnippetsDirectory(attributes); - } - finally { - System.clearProperty("maven.home"); - } - } - -} diff --git a/spring-restdocs-asciidoctor/src/test/resources/META-INF/services/org.asciidoctor.log.LogHandler b/spring-restdocs-asciidoctor/src/test/resources/META-INF/services/org.asciidoctor.log.LogHandler deleted file mode 100644 index 28aea3ae1..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/META-INF/services/org.asciidoctor.log.LogHandler +++ /dev/null @@ -1 +0,0 @@ -org.springframework.restdocs.asciidoctor.CapturingLogHandler \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/all-snippets.html b/spring-restdocs-asciidoctor/src/test/resources/operations/all-snippets.html deleted file mode 100644 index 508e45984..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/all-snippets.html +++ /dev/null @@ -1,67 +0,0 @@ -
-

Curl request

-
-
-
-
$ curl 'http://localhost:8080/' -i
-
-
-
-
-
-

Custom snippet

-
-
-
-
mycustomsnippet-äöü
-
-
-
-
-
-

HTTP request

-
-
-
-
GET / HTTP/1.1
-Host: localhost:8080
-
-
-
-
-
-

Response fields

-
- ----- - - - - - - - - - - - - - - - - - - - - - - - - -
PathTypeDescription

a

Object

one

a.b

Number

two

a.c

String

three

-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/built-in-snippet-custom-title.html b/spring-restdocs-asciidoctor/src/test/resources/operations/built-in-snippet-custom-title.html deleted file mode 100644 index a75533b3f..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/built-in-snippet-custom-title.html +++ /dev/null @@ -1,10 +0,0 @@ -
-

Example request

-
-
-
-
$ curl 'http://localhost:8080/' -i
-
-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/custom-snippet-custom-title.html b/spring-restdocs-asciidoctor/src/test/resources/operations/custom-snippet-custom-title.html deleted file mode 100644 index 0fed5ed2a..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/custom-snippet-custom-title.html +++ /dev/null @@ -1,10 +0,0 @@ -
-

Customized title

-
-
-
-
mycustomsnippet-äöü
-
-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/custom-snippet-default-title.html b/spring-restdocs-asciidoctor/src/test/resources/operations/custom-snippet-default-title.html deleted file mode 100644 index f5613259f..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/custom-snippet-default-title.html +++ /dev/null @@ -1,10 +0,0 @@ -
-

Custom snippet

-
-
-
-
mycustomsnippet-äöü
-
-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/missing-operation.html b/spring-restdocs-asciidoctor/src/test/resources/operations/missing-operation.html deleted file mode 100644 index 3c4cbbc83..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/missing-operation.html +++ /dev/null @@ -1,3 +0,0 @@ -
-

No snippets found for operation::missing-operation

-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/missing-snippet.html b/spring-restdocs-asciidoctor/src/test/resources/operations/missing-snippet.html deleted file mode 100644 index 70bf737ca..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/missing-snippet.html +++ /dev/null @@ -1,8 +0,0 @@ -
-

Missing snippet

-
-
-

Snippet missing-snippet not found for operation::some-operation

-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/multiple-snippets.html b/spring-restdocs-asciidoctor/src/test/resources/operations/multiple-snippets.html deleted file mode 100644 index 89b8ffba5..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/multiple-snippets.html +++ /dev/null @@ -1,21 +0,0 @@ -
-

Curl request

-
-
-
-
$ curl 'http://localhost:8080/' -i
-
-
-
-
-
-

HTTP request

-
-
-
-
GET / HTTP/1.1
-Host: localhost:8080
-
-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-in-section.html b/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-in-section.html deleted file mode 100644 index a8ec3f471..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-in-section.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
-
-

Alpha

-
-
-
-
-

1. B

-
-
-

Bravo

-
-
-

1.1. Curl request

-
-
-
$ curl 'http://localhost:8080/' -i
-
-
-
-
-
-
-

2. C

-
- -
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-simple.html b/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-simple.html deleted file mode 100644 index 35a3fa548..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-simple.html +++ /dev/null @@ -1,10 +0,0 @@ -
-

Curl request

-
-
-
-
$ curl 'http://localhost:8080/' -i
-
-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-table.html b/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-table.html deleted file mode 100644 index b01bc522a..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-table.html +++ /dev/null @@ -1,36 +0,0 @@ -
-

Response fields

-
- ----- - - - - - - - - - - - - - - - - - - - - - - - - -
PathTypeDescription

a

Object

one

a.b

Number

two

a.c

String

three

-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-with-level.html b/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-with-level.html deleted file mode 100644 index 727ccf024..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/operations/snippet-with-level.html +++ /dev/null @@ -1,8 +0,0 @@ -
-

Curl request

-
-
-
$ curl 'http://localhost:8080/' -i
-
-
-
\ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/sample-snippet.adoc b/spring-restdocs-asciidoctor/src/test/resources/sample-snippet.adoc deleted file mode 100644 index 0a3d1fa7e..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/sample-snippet.adoc +++ /dev/null @@ -1,4 +0,0 @@ -[source,bash] ----- -$ curl 'http://localhost:8080/' -i -H 'Accept: application/hal+json' ----- \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/some-operation/curl-request.adoc b/spring-restdocs-asciidoctor/src/test/resources/some-operation/curl-request.adoc deleted file mode 100644 index 0183405fd..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/some-operation/curl-request.adoc +++ /dev/null @@ -1,4 +0,0 @@ -[source,bash] ----- -$ curl 'http://localhost:8080/' -i ----- \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/some-operation/custom-snippet.adoc b/spring-restdocs-asciidoctor/src/test/resources/some-operation/custom-snippet.adoc deleted file mode 100644 index 7933144b1..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/some-operation/custom-snippet.adoc +++ /dev/null @@ -1,4 +0,0 @@ -[source,http,options="nowrap"] ----- -mycustomsnippet-äöü ----- \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/some-operation/http-request.adoc b/spring-restdocs-asciidoctor/src/test/resources/some-operation/http-request.adoc deleted file mode 100644 index 2034fb60a..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/some-operation/http-request.adoc +++ /dev/null @@ -1,6 +0,0 @@ -[source,http,options="nowrap"] ----- -GET / HTTP/1.1 -Host: localhost:8080 - ----- \ No newline at end of file diff --git a/spring-restdocs-asciidoctor/src/test/resources/some-operation/response-fields.adoc b/spring-restdocs-asciidoctor/src/test/resources/some-operation/response-fields.adoc deleted file mode 100644 index 9c0dcd213..000000000 --- a/spring-restdocs-asciidoctor/src/test/resources/some-operation/response-fields.adoc +++ /dev/null @@ -1,16 +0,0 @@ -|=== -|Path|Type|Description - -|`a` -|`Object` -|one - -|`a.b` -|`Number` -|two - -|`a.c` -|`String` -|three - -|=== \ No newline at end of file diff --git a/spring-restdocs-bom/build.gradle b/spring-restdocs-bom/build.gradle deleted file mode 100644 index bd0b4ce5c..000000000 --- a/spring-restdocs-bom/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -plugins { - id "java-platform" - id "maven-publish" -} - -description = "Spring REST Docs Bill of Materials" - -dependencies { - constraints { - api(project(":spring-restdocs-asciidoctor")) - api(project(":spring-restdocs-core")) - api(project(":spring-restdocs-mockmvc")) - api(project(":spring-restdocs-restassured")) - api(project(":spring-restdocs-webtestclient")) - } -} \ No newline at end of file diff --git a/spring-restdocs-core/build.gradle b/spring-restdocs-core/build.gradle deleted file mode 100644 index 496bb1a39..000000000 --- a/spring-restdocs-core/build.gradle +++ /dev/null @@ -1,96 +0,0 @@ -plugins { - id "java-library" - id "java-test-fixtures" - id "maven-publish" - id "optional-dependencies" -} - -description = "Spring REST Docs Core" - -configurations { - jarjar - jmustache - testArtifacts.extendsFrom testRuntime -} - -task jmustacheRepackJar(type: Jar) { repackJar -> - repackJar.archiveBaseName = "restdocs-jmustache-repack" - repackJar.archiveVersion = jmustacheVersion - - doLast() { - ant { - taskdef name: "jarjar", classname: "com.tonicsystems.jarjar.JarJarTask", - classpath: configurations.jarjar.asPath - jarjar(destfile: repackJar.archiveFile.get()) { - configurations.jmustache.each { originalJar -> - zipfileset(src: originalJar, includes: "**/*.class") - } - rule(pattern: "com.samskivert.**", result: "org.springframework.restdocs.@1") - } - } - } -} - -dependencies { - compileOnly("org.apiguardian:apiguardian-api") - - implementation("com.fasterxml.jackson.core:jackson-databind") - implementation("org.springframework:spring-web") - implementation(files(jmustacheRepackJar)) - - internal(platform(project(":spring-restdocs-platform"))) - - jarjar("com.googlecode.jarjar:jarjar:1.3") - - jmustache(platform(project(":spring-restdocs-platform"))) - jmustache("com.samskivert:jmustache@jar") - - optional(platform(project(":spring-restdocs-platform"))) - optional("jakarta.validation:jakarta.validation-api") - optional("org.hibernate.validator:hibernate-validator") - optional("org.junit.jupiter:junit-jupiter-api") - - testFixturesApi(platform(project(":spring-restdocs-platform"))) - testFixturesApi("org.assertj:assertj-core") - testFixturesApi("org.junit.jupiter:junit-jupiter") - testFixturesApi("org.mockito:mockito-core") - - testFixturesCompileOnly("org.apiguardian:apiguardian-api") - - testFixturesImplementation(files(jmustacheRepackJar)) - testFixturesImplementation("org.hamcrest:hamcrest-library") - testFixturesImplementation("org.mockito:mockito-core") - testFixturesImplementation("org.springframework:spring-core") - testFixturesImplementation("org.springframework:spring-web") - - testFixturesRuntimeOnly("org.junit.platform:junit-platform-launcher") - - testCompileOnly("org.apiguardian:apiguardian-api") - - testImplementation("org.assertj:assertj-core") - testImplementation("org.javamoney:moneta") - testImplementation("org.mockito:mockito-core") - testImplementation("org.springframework:spring-test") - - testRuntimeOnly("org.apache.tomcat.embed:tomcat-embed-el") - testRuntimeOnly("org.junit.platform:junit-platform-engine") -} - -jar { - dependsOn jmustacheRepackJar - from(zipTree(jmustacheRepackJar.archiveFile.get())) { - include "org/springframework/restdocs/**" - } -} - -components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { - skip() -} - -components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { - skip() -} - -tasks.named("test") { - useJUnitPlatform() -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java deleted file mode 100644 index 037189541..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/ManualRestDocumentation.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import java.io.File; - -import org.junit.jupiter.api.extension.Extension; - -/** - * {@code ManualRestDocumentation} is used to manually manage the - * {@link RestDocumentationContext}. Primarly intended for use with TestNG, but suitable - * for use in any environment where manual management of the context is required. - *

- * Users of JUnit should use {@link RestDocumentationExtension} and take advantage of its - * {@link Extension}-based support for automatic management of the context. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public final class ManualRestDocumentation implements RestDocumentationContextProvider { - - private final File outputDirectory; - - private StandardRestDocumentationContext context; - - /** - * Creates a new {@code ManualRestDocumentation} instance that will generate snippets - * to <gradle/maven build path>/generated-snippets. - */ - public ManualRestDocumentation() { - this(getDefaultOutputDirectory()); - } - - /** - * Creates a new {@code ManualRestDocumentation} instance that will generate snippets - * to the given {@code outputDirectory}. - * @param outputDirectory the output directory - */ - public ManualRestDocumentation(String outputDirectory) { - this(new File(outputDirectory)); - } - - private ManualRestDocumentation(File outputDirectory) { - this.outputDirectory = outputDirectory; - } - - /** - * Notification that a test is about to begin. Creates a - * {@link RestDocumentationContext} for the test on the given {@code testClass} with - * the given {@code testMethodName}. Must be followed by a call to - * {@link #afterTest()} once the test has completed. - * @param testClass the test class - * @param testMethodName the name of the test method - * @throws IllegalStateException if a context has already be created - */ - public void beforeTest(Class testClass, String testMethodName) { - if (this.context != null) { - throw new IllegalStateException("Context already exists. Did you forget to call afterTest()?"); - } - this.context = new StandardRestDocumentationContext(testClass, testMethodName, this.outputDirectory); - } - - /** - * Notification that a test has completed. Clears the {@link RestDocumentationContext} - * that was previously established by a call to {@link #beforeTest(Class, String)}. - */ - public void afterTest() { - this.context = null; - } - - @Override - public RestDocumentationContext beforeOperation() { - this.context.getAndIncrementStepCount(); - return this.context; - } - - private static File getDefaultOutputDirectory() { - if (new File("pom.xml").exists()) { - return new File("target/generated-snippets"); - } - return new File("build/generated-snippets"); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationContext.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationContext.java deleted file mode 100644 index 74a1e0386..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationContext.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import java.io.File; - -/** - * {@code RestDocumentationContext} encapsulates the context in which the documentation of - * a RESTful API is being performed. - * - * @author Andy Wilkinson - */ -public interface RestDocumentationContext { - - /** - * Returns the class whose tests are currently executing. - * @return the test class - */ - Class getTestClass(); - - /** - * Returns the name of the test method that is currently executing. - * @return the name of the test method - */ - String getTestMethodName(); - - /** - * Returns the current step count. - * @return the current step count - */ - int getStepCount(); - - /** - * Returns the output directory to which generated snippets should be written. - * @return the output directory - */ - File getOutputDirectory(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationContextProvider.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationContextProvider.java deleted file mode 100644 index 0a83935e1..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationContextProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -/** - * A {@code RestDocumentationContextProvider} is used to provide access to the - * {@link RestDocumentationContext}. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public interface RestDocumentationContextProvider { - - /** - * Returns a {@link RestDocumentationContext} for the operation that is about to be - * performed. - * @return the context for the operation - */ - RestDocumentationContext beforeOperation(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationExtension.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationExtension.java deleted file mode 100644 index 4b7f108dd..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/RestDocumentationExtension.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.Extension; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolver; - -/** - * A JUnit Jupiter {@link Extension} used to automatically manage the - * {@link RestDocumentationContext}. - * - * @author Andy Wilkinson - */ -public class RestDocumentationExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver { - - private final String outputDirectory; - - /** - * Creates a new {@code RestDocumentationExtension} that will use the default output - * directory. - */ - public RestDocumentationExtension() { - this(null); - } - - /** - * Creates a new {@code RestDocumentationExtension} that will use the given - * {@code outputDirectory}. - * @param outputDirectory snippet output directory - * @since 2.0.4 - */ - public RestDocumentationExtension(String outputDirectory) { - this.outputDirectory = outputDirectory; - } - - @Override - public void beforeEach(ExtensionContext context) throws Exception { - this.getDelegate(context).beforeTest(context.getRequiredTestClass(), context.getRequiredTestMethod().getName()); - } - - @Override - public void afterEach(ExtensionContext context) throws Exception { - this.getDelegate(context).afterTest(); - } - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - if (isTestMethodContext(extensionContext)) { - return RestDocumentationContextProvider.class.isAssignableFrom(parameterContext.getParameter().getType()); - } - return false; - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext context) { - return (RestDocumentationContextProvider) () -> getDelegate(context).beforeOperation(); - } - - private boolean isTestMethodContext(ExtensionContext context) { - return context.getTestClass().isPresent() && context.getTestMethod().isPresent(); - } - - private ManualRestDocumentation getDelegate(ExtensionContext context) { - Namespace namespace = Namespace.create(getClass(), context.getUniqueId()); - return context.getStore(namespace) - .getOrComputeIfAbsent(ManualRestDocumentation.class, this::createManualRestDocumentation, - ManualRestDocumentation.class); - } - - private ManualRestDocumentation createManualRestDocumentation(Class key) { - if (this.outputDirectory != null) { - return new ManualRestDocumentation(this.outputDirectory); - } - else { - return new ManualRestDocumentation(); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/StandardRestDocumentationContext.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/StandardRestDocumentationContext.java deleted file mode 100644 index 2989e1fdb..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/StandardRestDocumentationContext.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import java.io.File; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Standard implementation of {@link RestDocumentationContext}. - * - * @author Andy Wilkinson - */ -final class StandardRestDocumentationContext implements RestDocumentationContext { - - private final AtomicInteger stepCount = new AtomicInteger(0); - - private final Class testClass; - - private final String testMethodName; - - private final File outputDirectory; - - StandardRestDocumentationContext(Class testClass, String testMethodName, File outputDirectory) { - this.testClass = testClass; - this.testMethodName = testMethodName; - this.outputDirectory = outputDirectory; - } - - @Override - public Class getTestClass() { - return this.testClass; - } - - @Override - public String getTestMethodName() { - return this.testMethodName; - } - - int getAndIncrementStepCount() { - return this.stepCount.getAndIncrement(); - } - - @Override - public int getStepCount() { - return this.stepCount.get(); - } - - @Override - public File getOutputDirectory() { - return this.outputDirectory; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliDocumentation.java deleted file mode 100644 index 94531be12..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliDocumentation.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.util.Map; - -import org.springframework.restdocs.snippet.Snippet; - -/** - * Static factory methods for documenting a RESTful API as if it were being driven using a - * command-line utility such as curl or HTTPie. - * - * @author Andy Wilkinson - * @author Paul-Christian Volkmer - * @author Raman Gupta - * @author Tomasz Kopczynski - * @since 1.1.0 - */ -public abstract class CliDocumentation { - - static final CommandFormatter DEFAULT_COMMAND_FORMATTER = multiLineFormat(); - - private CliDocumentation() { - - } - - /** - * Returns a new {@code Snippet} that will document the curl request for the API - * operation. - * @return the snippet that will document the curl request - */ - public static Snippet curlRequest() { - return curlRequest(DEFAULT_COMMAND_FORMATTER); - } - - /** - * Returns a new {@code Snippet} that will document the curl request for the API - * operation. The given {@code attributes} will be available during snippet - * generation. - * @param attributes the attributes - * @return the snippet that will document the curl request - */ - public static Snippet curlRequest(Map attributes) { - return curlRequest(attributes, DEFAULT_COMMAND_FORMATTER); - } - - /** - * Returns a new {@code Snippet} that will document the curl request for the API - * operation. The given {@code commandFormatter} will be used to format the curl - * command in the snippet. - * @param commandFormatter the command formatter - * @return the snippet that will document the curl request - * @since 1.2.0 - */ - public static Snippet curlRequest(CommandFormatter commandFormatter) { - return curlRequest(null, commandFormatter); - } - - /** - * Returns a new {@code Snippet} that will document the curl request for the API - * operation. The given {@code attributes} will be available during snippet - * generation. The given {@code commandFormatter} will be used to format the curl - * command in the snippet. - * @param attributes the attributes - * @param commandFormatter the command formatter - * @return the snippet that will document the curl request - * @since 1.2.0 - */ - public static Snippet curlRequest(Map attributes, CommandFormatter commandFormatter) { - return new CurlRequestSnippet(attributes, commandFormatter); - } - - /** - * Returns a new {@code Snippet} that will document the HTTPie request for the API - * operation. - * @return the snippet that will document the HTTPie request - */ - public static Snippet httpieRequest() { - return httpieRequest(DEFAULT_COMMAND_FORMATTER); - } - - /** - * Returns a new {@code Snippet} that will document the HTTPie request for the API - * operation. The given {@code attributes} will be available during snippet - * generation. - * @param attributes the attributes - * @return the snippet that will document the HTTPie request - */ - public static Snippet httpieRequest(Map attributes) { - return httpieRequest(attributes, DEFAULT_COMMAND_FORMATTER); - } - - /** - * Returns a new {@code Snippet} that will document the HTTPie request for the API - * operation. The given {@code commandFormatter} will be used to format the HTTPie - * command in the snippet. - * @param commandFormatter the command formatter - * @return the snippet that will document the HTTPie request - * @since 1.2.0 - */ - public static Snippet httpieRequest(CommandFormatter commandFormatter) { - return httpieRequest(null, commandFormatter); - } - - /** - * Returns a new {@code Snippet} that will document the HTTPie request for the API - * operation. The given {@code attributes} will be available during snippet - * generation. The given {@code commandFormatter} will be used to format the HTTPie - * command in the snippet snippet. - * @param attributes the attributes - * @param commandFormatter the command formatter - * @return the snippet that will document the HTTPie request - * @since 1.2.0 - */ - public static Snippet httpieRequest(Map attributes, CommandFormatter commandFormatter) { - return new HttpieRequestSnippet(attributes, commandFormatter); - } - - /** - * Creates a new {@code CommandFormatter} that produces multi-line output. - * @return a multi-line {@code CommandFormatter} - */ - public static CommandFormatter multiLineFormat() { - return new ConcatenatingCommandFormatter(" \\%n "); - } - - /** - * Creates a new {@code CommandFormatter} that produces single-line output. - * @return a single-line {@code CommandFormatter} - */ - public static CommandFormatter singleLineFormat() { - return new ConcatenatingCommandFormatter(" "); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java deleted file mode 100644 index efc21ba4e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CliOperationRequest.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.net.URI; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.RequestCookie; - -/** - * An {@link OperationRequest} wrapper with methods that are useful when producing a - * snippet containing a CLI command for a request. - * - * @author Andy Wilkinson - * @author Raman Gupta - */ -final class CliOperationRequest implements OperationRequest { - - private final Set headerFilters; - - private final OperationRequest delegate; - - CliOperationRequest(OperationRequest delegate) { - this.delegate = delegate; - this.headerFilters = new HashSet<>(Arrays.asList(new NamedHeaderFilter(HttpHeaders.CONTENT_LENGTH), - new BasicAuthHeaderFilter(), new HostHeaderFilter(delegate.getUri()))); - } - - boolean isPutOrPost() { - return HttpMethod.PUT.equals(this.delegate.getMethod()) || HttpMethod.POST.equals(this.delegate.getMethod()); - } - - String getBasicAuthCredentials() { - List headerValue = this.delegate.getHeaders().get(HttpHeaders.AUTHORIZATION); - if (BasicAuthHeaderFilter.isBasicAuthHeader(headerValue)) { - return BasicAuthHeaderFilter.decodeBasicAuthHeader(headerValue); - } - return null; - } - - @Override - public byte[] getContent() { - return this.delegate.getContent(); - } - - @Override - public String getContentAsString() { - return this.delegate.getContentAsString(); - } - - @Override - public HttpHeaders getHeaders() { - HttpHeaders filteredHeaders = new HttpHeaders(); - for (Entry> header : this.delegate.getHeaders().headerSet()) { - if (allowedHeader(header)) { - filteredHeaders.put(header.getKey(), header.getValue()); - } - } - return HttpHeaders.readOnlyHttpHeaders(filteredHeaders); - } - - private boolean allowedHeader(Map.Entry> header) { - for (HeaderFilter headerFilter : this.headerFilters) { - if (!headerFilter.allow(header.getKey(), header.getValue())) { - return false; - } - } - if (HttpHeaders.HOST.equalsIgnoreCase(header.getKey()) && (!header.getValue().isEmpty())) { - String value = header.getValue().get(0); - if (value.equals(this.delegate.getUri().getHost() + ":" + this.delegate.getUri().getPort())) { - return false; - } - } - return true; - } - - @Override - public HttpMethod getMethod() { - return this.delegate.getMethod(); - } - - @Override - public Collection getParts() { - return this.delegate.getParts(); - } - - @Override - public URI getUri() { - return this.delegate.getUri(); - } - - @Override - public Collection getCookies() { - return this.delegate.getCookies(); - } - - private interface HeaderFilter { - - boolean allow(String name, List value); - - } - - private static final class BasicAuthHeaderFilter implements HeaderFilter { - - @Override - public boolean allow(String name, List value) { - return !(HttpHeaders.AUTHORIZATION.equals(name) && isBasicAuthHeader(value)); - } - - static boolean isBasicAuthHeader(List value) { - return value != null && (!value.isEmpty()) && value.get(0).startsWith("Basic "); - } - - static String decodeBasicAuthHeader(List value) { - return new String(Base64.getDecoder().decode(value.get(0).substring(6))); - } - - } - - private static final class NamedHeaderFilter implements HeaderFilter { - - private final String name; - - NamedHeaderFilter(String name) { - this.name = name; - } - - @Override - public boolean allow(String name, List value) { - return !this.name.equalsIgnoreCase(name); - } - - } - - private static final class HostHeaderFilter implements HeaderFilter { - - private final URI uri; - - private HostHeaderFilter(URI uri) { - this.uri = uri; - } - - @Override - public boolean allow(String name, List value) { - return !(value.isEmpty() || this.getImplicitHostHeader().equals(value.get(0))); - } - - private String getImplicitHostHeader() { - return this.uri.getHost() + ((this.uri.getPort() == -1) ? "" : ":" + this.uri.getPort()); - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CommandFormatter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CommandFormatter.java deleted file mode 100644 index b3d6c1e68..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CommandFormatter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.util.List; - -/** - * Formatter for CLI commands such as those included in {@link CurlRequestSnippet} and - * {@link HttpieRequestSnippet}. - * - * @author Tomasz Kopczynski - * @since 1.2.0 - */ -public interface CommandFormatter { - - /** - * Formats a list of {@code elements} into a single {@code String}. - * @param elements the {@code String} elements to be formatted - * @return a single formatted {@code String} - */ - String format(List elements); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatter.java deleted file mode 100644 index 412b8c00c..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.util.List; - -import org.springframework.util.CollectionUtils; - -/** - * {@link CommandFormatter} which concatenates commands with a given {@code separator}. - * - * @author Tomasz Kopczynski - */ -final class ConcatenatingCommandFormatter implements CommandFormatter { - - private String separator; - - ConcatenatingCommandFormatter(String separator) { - this.separator = separator; - } - - /** - * Concatenates a list of {@code String}s with a specified separator. - * @param elements a list of {@code String}s to be concatenated - * @return concatenated list of {@code String}s as one {@code String} - */ - @Override - public String format(List elements) { - if (CollectionUtils.isEmpty(elements)) { - return ""; - } - StringBuilder result = new StringBuilder(); - for (String element : elements) { - result.append(String.format(this.separator)); - result.append(element); - } - return result.toString(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java deleted file mode 100644 index 469973aee..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * A {@link Snippet} that documents the curl command for a request. - * - * @author Andy Wilkinson - * @author Paul-Christian Volkmer - * @author Tomasz Kopczynski - * @since 1.1.0 - * @see CliDocumentation#curlRequest() - * @see CliDocumentation#curlRequest(CommandFormatter) - * @see CliDocumentation#curlRequest(Map) - */ -public class CurlRequestSnippet extends TemplatedSnippet { - - private final CommandFormatter commandFormatter; - - /** - * Creates a new {@code CurlRequestSnippet} that will use the given - * {@code commandFormatter} to format the curl command. - * @param commandFormatter the formatter - */ - protected CurlRequestSnippet(CommandFormatter commandFormatter) { - this(null, commandFormatter); - } - - /** - * Creates a new {@code CurlRequestSnippet} with the given additional - * {@code attributes} that will be included in the model during template rendering. - * The given {@code commandFormaatter} will be used to format the curl command. - * @param attributes the additional attributes - * @param commandFormatter the formatter for generating the snippet - */ - protected CurlRequestSnippet(Map attributes, CommandFormatter commandFormatter) { - super("curl-request", attributes); - Assert.notNull(commandFormatter, "Command formatter must not be null"); - this.commandFormatter = commandFormatter; - } - - @Override - protected Map createModel(Operation operation) { - Map model = new HashMap<>(); - model.put("url", getUrl(operation)); - model.put("options", getOptions(operation)); - return model; - } - - private String getUrl(Operation operation) { - OperationRequest request = operation.getRequest(); - return String.format("'%s'", request.getUri()); - } - - private String getOptions(Operation operation) { - StringBuilder builder = new StringBuilder(); - writeIncludeHeadersInOutputOption(builder); - - CliOperationRequest request = new CliOperationRequest(operation.getRequest()); - writeUserOptionIfNecessary(request, builder); - writeHttpMethod(request, builder); - - List additionalLines = new ArrayList<>(); - writeHeaders(request, additionalLines); - writeCookies(request, additionalLines); - writePartsIfNecessary(request, additionalLines); - writeContent(request, additionalLines); - - builder.append(this.commandFormatter.format(additionalLines)); - - return builder.toString(); - } - - private void writeCookies(CliOperationRequest request, List lines) { - if (!CollectionUtils.isEmpty(request.getCookies())) { - StringBuilder cookiesBuilder = new StringBuilder(); - for (RequestCookie cookie : request.getCookies()) { - if (cookiesBuilder.length() > 0) { - cookiesBuilder.append(";"); - } - cookiesBuilder.append(String.format("%s=%s", cookie.getName(), cookie.getValue())); - } - lines.add(String.format("--cookie '%s'", cookiesBuilder.toString())); - } - } - - private void writeIncludeHeadersInOutputOption(StringBuilder builder) { - builder.append("-i"); - } - - private void writeUserOptionIfNecessary(CliOperationRequest request, StringBuilder builder) { - String credentials = request.getBasicAuthCredentials(); - if (credentials != null) { - builder.append(String.format(" -u '%s'", credentials)); - } - } - - private void writeHttpMethod(OperationRequest request, StringBuilder builder) { - builder.append(String.format(" -X %s", request.getMethod())); - } - - private void writeHeaders(CliOperationRequest request, List lines) { - for (Entry> entry : request.getHeaders().headerSet()) { - for (String header : entry.getValue()) { - if (StringUtils.hasText(request.getContentAsString()) && HttpHeaders.CONTENT_TYPE.equals(entry.getKey()) - && MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) { - continue; - } - lines.add(String.format("-H '%s: %s'", entry.getKey(), header)); - } - } - } - - private void writePartsIfNecessary(OperationRequest request, List lines) { - for (OperationRequestPart part : request.getParts()) { - StringBuilder oneLine = new StringBuilder(); - oneLine.append(String.format("-F '%s=", part.getName())); - if (!StringUtils.hasText(part.getSubmittedFileName())) { - oneLine.append(part.getContentAsString()); - } - else { - oneLine.append(String.format("@%s", part.getSubmittedFileName())); - } - if (part.getHeaders().getContentType() != null) { - oneLine.append(";type="); - oneLine.append(part.getHeaders().getContentType().toString()); - } - oneLine.append("'"); - lines.add(oneLine.toString()); - } - } - - private void writeContent(CliOperationRequest request, List lines) { - String content = request.getContentAsString(); - if (StringUtils.hasText(content)) { - lines.add(String.format("-d '%s'", content)); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java deleted file mode 100644 index 8b92fa0bf..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.FormParameters; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * A {@link Snippet} that documents the HTTPie command for a request. - * - * @author Raman Gupta - * @author Andy Wilkinson - * @author Tomasz Kopczynski - * @since 1.1.0 - * @see CliDocumentation#httpieRequest() - * @see CliDocumentation#httpieRequest(Map) - */ -public class HttpieRequestSnippet extends TemplatedSnippet { - - private final CommandFormatter commandFormatter; - - /** - * Creates a new {@code HttpieRequestSnippet} that will use the given - * {@code commandFormatter} to format the HTTPie command. - * @param commandFormatter the formatter - */ - protected HttpieRequestSnippet(CommandFormatter commandFormatter) { - this(null, commandFormatter); - } - - /** - * Creates a new {@code HttpieRequestSnippet} with the given additional - * {@code attributes} that will be included in the model during template rendering. - * The given {@code commandFormaatter} will be used to format the HTTPie command. - * @param attributes the additional attributes - * @param commandFormatter the formatter for generating the snippet - */ - protected HttpieRequestSnippet(Map attributes, CommandFormatter commandFormatter) { - super("httpie-request", attributes); - Assert.notNull(commandFormatter, "Command formatter must not be null"); - this.commandFormatter = commandFormatter; - } - - @Override - protected Map createModel(Operation operation) { - Map model = new HashMap<>(); - CliOperationRequest request = new CliOperationRequest(operation.getRequest()); - model.put("echoContent", getContentStandardIn(request)); - model.put("options", getOptions(request)); - model.put("url", getUrl(request)); - model.put("requestItems", getRequestItems(request)); - return model; - } - - private Object getContentStandardIn(CliOperationRequest request) { - if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType())) { - return ""; - } - String content = request.getContentAsString(); - if (StringUtils.hasText(content)) { - return String.format("echo '%s' | ", content); - } - return ""; - } - - private String getOptions(CliOperationRequest request) { - StringWriter options = new StringWriter(); - PrintWriter printer = new PrintWriter(options); - writeOptions(request, printer); - writeUserOptionIfNecessary(request, printer); - writeMethodIfNecessary(request, printer); - return options.toString(); - } - - private String getUrl(OperationRequest request) { - return String.format("'%s'", request.getUri()); - } - - private String getRequestItems(CliOperationRequest request) { - List lines = new ArrayList<>(); - - writeHeaders(request, lines); - writeCookies(request, lines); - writeFormDataIfNecessary(request, lines); - - return this.commandFormatter.format(lines); - } - - private void writeOptions(OperationRequest request, PrintWriter writer) { - if (!request.getParts().isEmpty()) { - writer.print("--multipart "); - } - else if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType())) { - writer.print("--form "); - } - } - - private void writeUserOptionIfNecessary(CliOperationRequest request, PrintWriter writer) { - String credentials = request.getBasicAuthCredentials(); - if (credentials != null) { - writer.print(String.format("--auth '%s' ", credentials)); - } - } - - private void writeMethodIfNecessary(OperationRequest request, PrintWriter writer) { - writer.print(String.format("%s", request.getMethod().name())); - } - - private void writeFormDataIfNecessary(OperationRequest request, List lines) { - if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType())) { - FormParameters.from(request) - .forEach((key, values) -> values.forEach((value) -> lines.add(String.format("'%s=%s'", key, value)))); - } - else { - for (OperationRequestPart part : request.getParts()) { - StringBuilder oneLine = new StringBuilder(); - oneLine.append(String.format("'%s'", part.getName())); - if (!StringUtils.hasText(part.getSubmittedFileName())) { - oneLine.append(String.format("='%s'", part.getContentAsString())); - } - else { - oneLine.append(String.format("@'%s'", part.getSubmittedFileName())); - } - - lines.add(oneLine.toString()); - } - } - } - - private void writeHeaders(OperationRequest request, List lines) { - HttpHeaders headers = request.getHeaders(); - for (Entry> entry : headers.headerSet()) { - if (entry.getKey().equals(HttpHeaders.CONTENT_TYPE) - && headers.getContentType().isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) { - continue; - } - for (String header : entry.getValue()) { - // HTTPie adds Content-Type automatically with --form - if (!request.getParts().isEmpty() && entry.getKey().equals(HttpHeaders.CONTENT_TYPE) - && header.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) { - continue; - } - lines.add(String.format("'%s:%s'", entry.getKey(), header)); - } - } - } - - private void writeCookies(OperationRequest request, List lines) { - for (RequestCookie cookie : request.getCookies()) { - lines.add(String.format("'Cookie:%s=%s'", cookie.getName(), cookie.getValue())); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/package-info.java deleted file mode 100644 index ff30b8208..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting CLI commands required to make a request to a RESTful API. - */ -package org.springframework.restdocs.cli; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractConfigurer.java deleted file mode 100644 index 39bfd6d3f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractConfigurer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -import java.util.Map; - -import org.springframework.restdocs.RestDocumentationContext; - -/** - * Abstract configurer that declares methods that are internal to the documentation - * configuration implementation. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public abstract class AbstractConfigurer { - - /** - * Applies the configurer to the given {@code configuration}. - * @param configuration the configuration to be configured - * @param context the current documentation context - */ - public abstract void apply(Map configuration, RestDocumentationContext context); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractNestedConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractNestedConfigurer.java deleted file mode 100644 index c5327c864..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/AbstractNestedConfigurer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -/** - * Base class for {@link NestedConfigurer} implementations. - * - * @param the type of the configurer's parent - * @author Andy Wilkinson - * @since 1.1.0 - */ -public abstract class AbstractNestedConfigurer extends AbstractConfigurer implements NestedConfigurer { - - private final PARENT parent; - - /** - * Creates a new {@code AbstractNestedConfigurer} with the given {@code parent}. - * @param parent the parent - */ - protected AbstractNestedConfigurer(PARENT parent) { - this.parent = parent; - } - - @Override - public final PARENT and() { - return this.parent; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/NestedConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/NestedConfigurer.java deleted file mode 100644 index 8f26d2f95..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/NestedConfigurer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -/** - * A configurer that is nested and, therefore, has a parent. - * - * @param the parent's type - * @author Andy Wilkinson - * @since 1.1.0 - */ -interface NestedConfigurer { - - /** - * Returns the configurer's parent. - * @return the parent - */ - PARENT and(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/OperationPreprocessorsConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/OperationPreprocessorsConfigurer.java deleted file mode 100644 index 56b5b5ed9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/OperationPreprocessorsConfigurer.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -import java.util.Map; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.preprocess.OperationPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.operation.preprocess.Preprocessors; - -/** - * A configurer that can be used to configure the default operation preprocessors. - * - * @param the type of the configurer's parent - * @param the concrete type of the configurer to be returned from chained methods - * @author Filip Hrisafov - * @author Andy Wilkinson - * @since 2.0.0 - */ -public abstract class OperationPreprocessorsConfigurer extends AbstractNestedConfigurer { - - private OperationRequestPreprocessor defaultOperationRequestPreprocessor; - - private OperationResponsePreprocessor defaultOperationResponsePreprocessor; - - /** - * Creates a new {@code OperationPreprocessorConfigurer} with the given - * {@code parent}. - * @param parent the parent - */ - protected OperationPreprocessorsConfigurer(PARENT parent) { - super(parent); - } - - @Override - public void apply(Map configuration, RestDocumentationContext context) { - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR, - this.defaultOperationRequestPreprocessor); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR, - this.defaultOperationResponsePreprocessor); - } - - /** - * Configures the default operation request preprocessors. - * @param preprocessors the preprocessors - * @return {@code this} - */ - @SuppressWarnings("unchecked") - public TYPE withRequestDefaults(OperationPreprocessor... preprocessors) { - this.defaultOperationRequestPreprocessor = Preprocessors.preprocessRequest(preprocessors); - return (TYPE) this; - } - - /** - * Configures the default operation response preprocessors. - * @param preprocessors the preprocessors - * @return {@code this} - */ - @SuppressWarnings("unchecked") - public TYPE withResponseDefaults(OperationPreprocessor... preprocessors) { - this.defaultOperationResponsePreprocessor = Preprocessors.preprocessResponse(preprocessors); - return (TYPE) this; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java deleted file mode 100644 index 3c49684f7..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/RestDocumentationConfigurer.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.mustache.Mustache; -import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolverFactory; -import org.springframework.restdocs.snippet.StandardWriterResolver; -import org.springframework.restdocs.snippet.WriterResolver; -import org.springframework.restdocs.templates.StandardTemplateResourceResolver; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.mustache.AsciidoctorTableCellContentLambda; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; - -/** - * Abstract base class for the configuration of Spring REST Docs. - * - * @param the concrete type of the {@link SnippetConfigurer} - * @param

the concrete type of the {@link OperationPreprocessorsConfigurer} - * @param the concrete type of this configurer, to be returned from methods that - * support chaining - * @author Andy Wilkinson - * @author Filip Hrisafov - * @since 1.1.0 - */ -public abstract class RestDocumentationConfigurer { - - private final WriterResolverConfigurer writerResolverConfigurer = new WriterResolverConfigurer(); - - private final TemplateEngineConfigurer templateEngineConfigurer = new TemplateEngineConfigurer(); - - /** - * Returns a {@link SnippetConfigurer} that can be used to configure the snippets that - * will be generated. - * @return the snippet configurer - */ - public abstract S snippets(); - - /** - * Returns an {@link OperationPreprocessorsConfigurer} that can be used to configure - * the operation request and response preprocessors that will be used. - * @return the operation preprocessors configurer - */ - public abstract P operationPreprocessors(); - - /** - * Configures the {@link TemplateEngine} that will be used for snippet rendering. - * @param templateEngine the template engine to use - * @return {@code this} - */ - @SuppressWarnings("unchecked") - public final T templateEngine(TemplateEngine templateEngine) { - this.templateEngineConfigurer.setTemplateEngine(templateEngine); - return (T) this; - } - - /** - * Configures the {@link WriterResolver} that will be used to resolve a writer for a - * snippet. - * @param writerResolver the writer resolver to use - * @return {@code this} - */ - @SuppressWarnings("unchecked") - public final T writerResolver(WriterResolver writerResolver) { - this.writerResolverConfigurer.setWriterResolver(writerResolver); - return (T) this; - } - - /** - * Applies this configurer to the given {@code configuration} within the given - * {@code context}. - * @param configuration the configuration - * @param context the current context - */ - protected final void apply(Map configuration, RestDocumentationContext context) { - List configurers = Arrays.asList(snippets(), operationPreprocessors(), - this.templateEngineConfigurer, this.writerResolverConfigurer); - for (AbstractConfigurer configurer : configurers) { - configurer.apply(configuration, context); - } - } - - private static final class TemplateEngineConfigurer extends AbstractConfigurer { - - private TemplateEngine templateEngine; - - @Override - public void apply(Map configuration, RestDocumentationContext context) { - TemplateEngine engineToUse = this.templateEngine; - if (engineToUse == null) { - SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration - .get(SnippetConfiguration.class.getName()); - Map templateContext = new HashMap<>(); - if (snippetConfiguration.getTemplateFormat().getId().equals(TemplateFormats.asciidoctor().getId())) { - templateContext.put("tableCellContent", new AsciidoctorTableCellContentLambda()); - } - engineToUse = new MustacheTemplateEngine( - new StandardTemplateResourceResolver(snippetConfiguration.getTemplateFormat()), - Charset.forName(snippetConfiguration.getEncoding()), Mustache.compiler().escapeHTML(false), - templateContext); - } - configuration.put(TemplateEngine.class.getName(), engineToUse); - } - - private void setTemplateEngine(TemplateEngine templateEngine) { - this.templateEngine = templateEngine; - } - - } - - private static final class WriterResolverConfigurer extends AbstractConfigurer { - - private WriterResolver writerResolver; - - @Override - public void apply(Map configuration, RestDocumentationContext context) { - WriterResolver resolverToUse = this.writerResolver; - if (resolverToUse == null) { - SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration - .get(SnippetConfiguration.class.getName()); - resolverToUse = new StandardWriterResolver(new RestDocumentationContextPlaceholderResolverFactory(), - snippetConfiguration.getEncoding(), snippetConfiguration.getTemplateFormat()); - } - configuration.put(WriterResolver.class.getName(), resolverToUse); - } - - private void setWriterResolver(WriterResolver writerResolver) { - this.writerResolver = writerResolver; - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfiguration.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfiguration.java deleted file mode 100644 index b216d9e43..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfiguration.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -import org.springframework.restdocs.templates.TemplateFormat; - -/** - * An encapsulation of the configuration for documentation snippets. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -class SnippetConfiguration { - - private final String encoding; - - private final TemplateFormat format; - - SnippetConfiguration(String encoding, TemplateFormat templateFormat) { - this.encoding = encoding; - this.format = templateFormat; - } - - String getEncoding() { - return this.encoding; - } - - TemplateFormat getTemplateFormat() { - return this.format; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java deleted file mode 100644 index a21b63146..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/SnippetConfigurer.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.cli.CliDocumentation; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.http.HttpDocumentation; -import org.springframework.restdocs.payload.PayloadDocumentation; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; - -/** - * A configurer that can be used to configure the generated documentation snippets. - * - * @param the type of the configurer's parent - * @param the concrete type of the configurer to be returned from chained methods - * @author Andy Wilkinson - * @since 1.1.0 - */ -public abstract class SnippetConfigurer extends AbstractNestedConfigurer { - - private List defaultSnippets = new ArrayList<>(Arrays.asList(CliDocumentation.curlRequest(), - CliDocumentation.httpieRequest(), HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse(), - PayloadDocumentation.requestBody(), PayloadDocumentation.responseBody())); - - /** - * The default encoding for documentation snippets. - * - * @see #withEncoding(String) - */ - public static final String DEFAULT_SNIPPET_ENCODING = "UTF-8"; - - /** - * The default format for documentation snippets. - * - * @see #withTemplateFormat(TemplateFormat) - */ - public static final TemplateFormat DEFAULT_TEMPLATE_FORMAT = TemplateFormats.asciidoctor(); - - private String snippetEncoding = DEFAULT_SNIPPET_ENCODING; - - private TemplateFormat templateFormat = DEFAULT_TEMPLATE_FORMAT; - - /** - * Creates a new {@code SnippetConfigurer} with the given {@code parent}. - * @param parent the parent - */ - protected SnippetConfigurer(PARENT parent) { - super(parent); - } - - @Override - public void apply(Map configuration, RestDocumentationContext context) { - configuration.put(SnippetConfiguration.class.getName(), - new SnippetConfiguration(this.snippetEncoding, this.templateFormat)); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS, this.defaultSnippets); - } - - /** - * Configures any documentation snippets to be written using the given - * {@code encoding}. The default is UTF-8. - * @param encoding the encoding - * @return {@code this} - */ - @SuppressWarnings("unchecked") - public TYPE withEncoding(String encoding) { - this.snippetEncoding = encoding; - return (TYPE) this; - } - - /** - * Configures the documentation snippets that will be produced by default. - * @param defaultSnippets the default snippets - * @return {@code this} - * @see #withAdditionalDefaults(Snippet...) - */ - @SuppressWarnings("unchecked") - public TYPE withDefaults(Snippet... defaultSnippets) { - this.defaultSnippets = new ArrayList<>(Arrays.asList(defaultSnippets)); - return (TYPE) this; - } - - /** - * Configures additional documentation snippets that will be produced by default. - * @param additionalDefaultSnippets the additional default snippets - * @return {@code this} - * @see #withDefaults(Snippet...) - */ - @SuppressWarnings("unchecked") - public TYPE withAdditionalDefaults(Snippet... additionalDefaultSnippets) { - this.defaultSnippets.addAll(Arrays.asList(additionalDefaultSnippets)); - return (TYPE) this; - } - - /** - * Configures the format of the documentation snippet templates. - * @param format the snippet template format - * @return {@code this} - */ - @SuppressWarnings("unchecked") - public TYPE withTemplateFormat(TemplateFormat format) { - this.templateFormat = format; - return (TYPE) this; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/package-info.java deleted file mode 100644 index 0c92c51e0..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/config/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Classes for configuring Spring REST Docs. - */ -package org.springframework.restdocs.config; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/Constraint.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/Constraint.java deleted file mode 100644 index 3ad467432..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/Constraint.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.util.Map; - -/** - * A constraint. - * - * @author Andy Wilkinson - */ -public class Constraint { - - private final String name; - - private final Map configuration; - - /** - * Creates a new {@code Constraint} with the given {@code name} and - * {@code configuration}. - * @param name the name - * @param configuration the configuration - */ - public Constraint(String name, Map configuration) { - this.name = name; - this.configuration = configuration; - } - - /** - * Returns the name of the constraint. - * @return the name - */ - public String getName() { - return this.name; - } - - /** - * Returns the configuration of the constraint. - * @return the configuration - */ - public Map getConfiguration() { - return this.configuration; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintDescriptionResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintDescriptionResolver.java deleted file mode 100644 index 6291f9a87..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintDescriptionResolver.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -/** - * Resolves a description for a {@link Constraint}. - * - * @author Andy Wilkinson - */ -public interface ConstraintDescriptionResolver { - - /** - * Resolves the description for the given {@code constraint}. - * @param constraint the constraint - * @return the description - */ - String resolveDescription(Constraint constraint); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintDescriptions.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintDescriptions.java deleted file mode 100644 index 7df2bb0c9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintDescriptions.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Provides access to descriptions of a class's constraints. - * - * @author Andy Wilkinson - */ -public class ConstraintDescriptions { - - private final Class clazz; - - private final ConstraintResolver constraintResolver; - - private final ConstraintDescriptionResolver descriptionResolver; - - /** - * Create a new {@code ConstraintDescriptions} for the given {@code clazz}. - * Constraints will be resolved using a {@link ValidatorConstraintResolver} and - * descriptions will be resolved using a - * {@link ResourceBundleConstraintDescriptionResolver}. - * @param clazz the class - */ - public ConstraintDescriptions(Class clazz) { - this(clazz, new ValidatorConstraintResolver(), new ResourceBundleConstraintDescriptionResolver()); - } - - /** - * Create a new {@code ConstraintDescriptions} for the given {@code clazz}. - * Constraints will be resolved using the given {@code constraintResolver} and - * descriptions will be resolved using a - * {@link ResourceBundleConstraintDescriptionResolver}. - * @param clazz the class - * @param constraintResolver the constraint resolver - */ - public ConstraintDescriptions(Class clazz, ConstraintResolver constraintResolver) { - this(clazz, constraintResolver, new ResourceBundleConstraintDescriptionResolver()); - } - - /** - * Create a new {@code ConstraintDescriptions} for the given {@code clazz}. - * Constraints will be resolved using a {@link ValidatorConstraintResolver} and - * descriptions will be resolved using the given {@code descriptionResolver}. - * @param clazz the class - * @param descriptionResolver the description resolver - */ - public ConstraintDescriptions(Class clazz, ConstraintDescriptionResolver descriptionResolver) { - this(clazz, new ValidatorConstraintResolver(), descriptionResolver); - } - - /** - * Create a new {@code ConstraintDescriptions} for the given {@code clazz}. - * Constraints will be resolved using the given {@code constraintResolver} and - * descriptions will be resolved using the given {@code descriptionResolver}. - * @param clazz the class - * @param constraintResolver the constraint resolver - * @param descriptionResolver the description resolver - */ - public ConstraintDescriptions(Class clazz, ConstraintResolver constraintResolver, - ConstraintDescriptionResolver descriptionResolver) { - this.clazz = clazz; - this.constraintResolver = constraintResolver; - this.descriptionResolver = descriptionResolver; - } - - /** - * Returns a list of the descriptions for the constraints on the given property. - * @param property the property - * @return the list of constraint descriptions - */ - public List descriptionsForProperty(String property) { - List constraints = this.constraintResolver.resolveForProperty(property, this.clazz); - List descriptions = new ArrayList<>(); - for (Constraint constraint : constraints) { - descriptions.add(this.descriptionResolver.resolveDescription(constraint)); - } - Collections.sort(descriptions); - return descriptions; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintResolver.java deleted file mode 100644 index f5e6079d5..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ConstraintResolver.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.util.List; - -/** - * An abstraction for resolving a class's constraints. - * - * @author Andy Wilkinson - */ -public interface ConstraintResolver { - - /** - * Resolves and returns the constraints for the given {@code property} on the given - * {@code clazz}. If there are no constraints, an empty list is returned. - * @param property the property - * @param clazz the class - * @return the list of constraints, never {@code null} - */ - List resolveForProperty(String property, Class clazz); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java deleted file mode 100644 index 59788d061..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -import jakarta.validation.constraints.AssertFalse; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.DecimalMax; -import jakarta.validation.constraints.DecimalMin; -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.Future; -import jakarta.validation.constraints.FutureOrPresent; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.Negative; -import jakarta.validation.constraints.NegativeOrZero; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Null; -import jakarta.validation.constraints.Past; -import jakarta.validation.constraints.PastOrPresent; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Positive; -import jakarta.validation.constraints.PositiveOrZero; -import jakarta.validation.constraints.Size; -import org.hibernate.validator.constraints.CodePointLength; -import org.hibernate.validator.constraints.CreditCardNumber; -import org.hibernate.validator.constraints.Currency; -import org.hibernate.validator.constraints.EAN; -import org.hibernate.validator.constraints.Length; -import org.hibernate.validator.constraints.LuhnCheck; -import org.hibernate.validator.constraints.Mod10Check; -import org.hibernate.validator.constraints.Mod11Check; -import org.hibernate.validator.constraints.Range; -import org.hibernate.validator.constraints.URL; - -import org.springframework.util.PropertyPlaceholderHelper; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; -import org.springframework.util.StringUtils; - -/** - * A {@link ConstraintDescriptionResolver} that resolves constraint descriptions from a - * {@link ResourceBundle}. The resource bundle's keys are the name of the constraint with - * {@code .description} appended. For example, the key for the constraint named - * {@code jakarta.validation.constraints.NotNull} is - * {@code jakarta.validation.constraints.NotNull.description}. - *

- * Default descriptions are provided for all of Bean Validation 3.1's constraints: - * - *

    - *
  • {@link AssertFalse} - *
  • {@link AssertTrue} - *
  • {@link DecimalMax} - *
  • {@link DecimalMin} - *
  • {@link Digits} - *
  • {@link Email} - *
  • {@link Future} - *
  • {@link FutureOrPresent} - *
  • {@link Max} - *
  • {@link Min} - *
  • {@link Negative} - *
  • {@link NegativeOrZero} - *
  • {@link NotBlank} - *
  • {@link NotEmpty} - *
  • {@link NotNull} - *
  • {@link Null} - *
  • {@link Past} - *
  • {@link PastOrPresent} - *
  • {@link Pattern} - *
  • {@link Positive} - *
  • {@link PositiveOrZero} - *
  • {@link Size} - *
- * - *

- * Default descriptions are also provided for the following Hibernate Validator - * constraints: - * - *

    - *
  • {@link CodePointLength} - *
  • {@link CreditCardNumber} - *
  • {@link Currency} - *
  • {@link EAN} - *
  • {@link Length} - *
  • {@link LuhnCheck} - *
  • {@link Mod10Check} - *
  • {@link Mod11Check} - *
  • {@link Range} - *
  • {@link URL} - *
- * - * @author Andy Wilkinson - */ -public class ResourceBundleConstraintDescriptionResolver implements ConstraintDescriptionResolver { - - private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}"); - - private final ResourceBundle defaultDescriptions; - - private final ResourceBundle userDescriptions; - - /** - * Creates a new {@code ResourceBundleConstraintDescriptionResolver} that will resolve - * descriptions by looking them up in a resource bundle with the base name - * {@code org.springframework.restdocs.constraints.ConstraintDescriptions} in the - * default locale loaded using the thread context class loader. - */ - public ResourceBundleConstraintDescriptionResolver() { - this(getBundle("ConstraintDescriptions")); - } - - /** - * Creates a new {@code ResourceBundleConstraintDescriptionResolver} that will resolve - * descriptions by looking them up in the given {@code resourceBundle}. - * @param resourceBundle the resource bundle - */ - public ResourceBundleConstraintDescriptionResolver(ResourceBundle resourceBundle) { - this.defaultDescriptions = getBundle("DefaultConstraintDescriptions"); - this.userDescriptions = resourceBundle; - } - - private static ResourceBundle getBundle(String name) { - try { - return ResourceBundle.getBundle( - ResourceBundleConstraintDescriptionResolver.class.getPackage().getName() + "." + name, - Locale.getDefault(), Thread.currentThread().getContextClassLoader()); - } - catch (MissingResourceException ex) { - return null; - } - } - - @Override - public String resolveDescription(Constraint constraint) { - String key = constraint.getName() + ".description"; - return this.propertyPlaceholderHelper.replacePlaceholders(getDescription(key), - new ConstraintPlaceholderResolver(constraint)); - } - - private String getDescription(String key) { - try { - if (this.userDescriptions != null) { - return this.userDescriptions.getString(key); - } - } - catch (MissingResourceException ex) { - // Continue and return default description, if available - } - return this.defaultDescriptions.getString(key); - } - - private static final class ConstraintPlaceholderResolver implements PlaceholderResolver { - - private final Constraint constraint; - - private ConstraintPlaceholderResolver(Constraint constraint) { - this.constraint = constraint; - } - - @Override - public String resolvePlaceholder(String placeholderName) { - Object replacement = this.constraint.getConfiguration().get(placeholderName); - if (replacement == null) { - return null; - } - if (replacement.getClass().isArray()) { - return StringUtils.arrayToDelimitedString((Object[]) replacement, ", "); - } - return replacement.toString(); - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ValidatorConstraintResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ValidatorConstraintResolver.java deleted file mode 100644 index 28910a938..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ValidatorConstraintResolver.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.metadata.BeanDescriptor; -import jakarta.validation.metadata.ConstraintDescriptor; -import jakarta.validation.metadata.PropertyDescriptor; - -/** - * A {@link ConstraintResolver} that uses a Bean Validation {@link Validator} to resolve - * constraints. The name of the constraint is the fully-qualified class name of the - * constraint annotation. For example, a {@link NotNull} constraint will be named - * {@code jakarta.validation.constraints.NotNull}. - * - * @author Andy Wilkinson - * - */ -public class ValidatorConstraintResolver implements ConstraintResolver { - - private final Validator validator; - - /** - * Creates a new {@code ValidatorConstraintResolver} that will use a {@link Validator} - * in its default configuration to resolve constraints. - * - * @see Validation#buildDefaultValidatorFactory() - * @see ValidatorFactory#getValidator() - */ - public ValidatorConstraintResolver() { - this(Validation.buildDefaultValidatorFactory().getValidator()); - } - - /** - * Creates a new {@code ValidatorConstraintResolver} that will use the given - * {@code Validator} to resolve constraints. - * @param validator the validator - */ - public ValidatorConstraintResolver(Validator validator) { - this.validator = validator; - } - - @Override - public List resolveForProperty(String property, Class clazz) { - List constraints = new ArrayList<>(); - BeanDescriptor beanDescriptor = this.validator.getConstraintsForClass(clazz); - PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(property); - if (propertyDescriptor != null) { - for (ConstraintDescriptor constraintDescriptor : propertyDescriptor.getConstraintDescriptors()) { - constraints.add(new Constraint(constraintDescriptor.getAnnotation().annotationType().getName(), - constraintDescriptor.getAttributes())); - } - } - return constraints; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/package-info.java deleted file mode 100644 index 66adf0a6f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting a RESTful API's constraints. - */ -package org.springframework.restdocs.constraints; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/AbstractCookiesSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/AbstractCookiesSnippet.java deleted file mode 100644 index 19201586b..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/AbstractCookiesSnippet.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; - -/** - * Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that - * document a RESTful resource's request or response cookies. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - * @since 3.0 - */ -public abstract class AbstractCookiesSnippet extends TemplatedSnippet { - - private final Map descriptorsByName = new LinkedHashMap<>(); - - private final boolean ignoreUndocumentedCookies; - - /** - * Creates a new {@code AbstractCookiesSnippet} that will produce a snippet named - * {@code -cookies}. The cookies will be documented using the given - * {@code descriptors} and the given {@code attributes} will be included in the model - * during template rendering. - * @param type the type of the cookies - * @param descriptors the cookie descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedCookies whether undocumented cookies should be ignored - */ - protected AbstractCookiesSnippet(String type, List descriptors, Map attributes, - boolean ignoreUndocumentedCookies) { - super(type + "-cookies", attributes); - for (CookieDescriptor descriptor : descriptors) { - Assert.notNull(descriptor.getName(), "Cookie descriptors must have a name"); - if (!descriptor.isIgnored()) { - Assert.notNull(descriptor.getDescription(), "The descriptor for cookie '" + descriptor.getName() - + "' must either have a description or be marked as ignored"); - } - this.descriptorsByName.put(descriptor.getName(), descriptor); - } - this.ignoreUndocumentedCookies = ignoreUndocumentedCookies; - } - - @Override - protected Map createModel(Operation operation) { - verifyCookieDescriptors(operation); - - Map model = new HashMap<>(); - List> cookies = new ArrayList<>(); - for (CookieDescriptor descriptor : this.descriptorsByName.values()) { - if (!descriptor.isIgnored()) { - cookies.add(createModelForDescriptor(descriptor)); - } - } - model.put("cookies", cookies); - return model; - } - - private void verifyCookieDescriptors(Operation operation) { - Set actualCookies = extractActualCookies(operation); - Set expectedCookies = new HashSet<>(); - for (Entry entry : this.descriptorsByName.entrySet()) { - if (!entry.getValue().isOptional()) { - expectedCookies.add(entry.getKey()); - } - } - Set undocumentedCookies; - if (this.ignoreUndocumentedCookies) { - undocumentedCookies = Collections.emptySet(); - } - else { - undocumentedCookies = new HashSet<>(actualCookies); - undocumentedCookies.removeAll(this.descriptorsByName.keySet()); - } - Set missingCookies = new HashSet<>(expectedCookies); - missingCookies.removeAll(actualCookies); - - if (!undocumentedCookies.isEmpty() || !missingCookies.isEmpty()) { - verificationFailed(undocumentedCookies, missingCookies); - } - } - - /** - * Extracts the names of the cookies from the request or response of the given - * {@code operation}. - * @param operation the operation - * @return the cookie names - */ - protected abstract Set extractActualCookies(Operation operation); - - /** - * Called when the documented cookies do not match the actual cookies. - * @param undocumentedCookies the cookies that were found in the operation but were - * not documented - * @param missingCookies the cookies that were documented but were not found in the - * operation - */ - protected abstract void verificationFailed(Set undocumentedCookies, Set missingCookies); - - /** - * Returns the list of {@link CookieDescriptor CookieDescriptors} that will be used to - * generate the documentation. - * @return the cookie descriptors - */ - protected final Map getCookieDescriptors() { - return this.descriptorsByName; - } - - /** - * Returns whether or not this snippet ignores undocumented cookies. - * @return {@code true} if undocumented cookies are ignored, otherwise {@code false} - */ - protected final boolean isIgnoreUndocumentedCookies() { - return this.ignoreUndocumentedCookies; - } - - /** - * Returns a model for the given {@code descriptor}. - * @param descriptor the descriptor - * @return the model - */ - protected Map createModelForDescriptor(CookieDescriptor descriptor) { - Map model = new HashMap<>(); - model.put("name", descriptor.getName()); - model.put("description", descriptor.getDescription()); - model.put("optional", descriptor.isOptional()); - model.putAll(descriptor.getAttributes()); - return model; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/CookieDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/CookieDescriptor.java deleted file mode 100644 index 97ee508c8..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/CookieDescriptor.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import org.springframework.restdocs.snippet.IgnorableDescriptor; - -/** - * A description of a cookie found in a request or response. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - * @since 3.0 - * @see CookieDocumentation#cookieWithName(String) - */ -public class CookieDescriptor extends IgnorableDescriptor { - - private final String name; - - private boolean optional; - - /** - * Creates a new {@code CookieDescriptor} describing the cookie with the given - * {@code name}. - * @param name the name - */ - protected CookieDescriptor(String name) { - this.name = name; - } - - /** - * Marks the cookie as optional. - * @return {@code this} - */ - public final CookieDescriptor optional() { - this.optional = true; - return this; - } - - /** - * Returns the name for the cookie. - * @return the cookie name - */ - public final String getName() { - return this.name; - } - - /** - * Returns {@code true} if the described cookie is optional, otherwise {@code false}. - * @return {@code true} if the described cookie is optional, otherwise {@code false} - */ - public final boolean isOptional() { - return this.optional; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/CookieDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/CookieDocumentation.java deleted file mode 100644 index 20c20f43f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/CookieDocumentation.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.snippet.Snippet; - -/** - * Static factory methods for documenting a RESTful API's request and response cookies. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - * @since 3.0 - */ -public abstract class CookieDocumentation { - - private CookieDocumentation() { - - } - - /** - * Creates a {@code CookieDescriptor} that describes a cookie with the given - * {@code name}. - * @param name the name of the cookie - * @return a {@code CookieDescriptor} ready for further configuration - */ - public static CookieDescriptor cookieWithName(String name) { - return new CookieDescriptor(name); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * request. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet requestCookies(CookieDescriptor... descriptors) { - return requestCookies(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * request. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet requestCookies(List descriptors) { - return new RequestCookiesSnippet(descriptors); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * request. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented cookies will be ignored. - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet relaxedRequestCookies(CookieDescriptor... descriptors) { - return relaxedRequestCookies(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * request. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented cookies will be ignored. - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet relaxedRequestCookies(List descriptors) { - return new RequestCookiesSnippet(descriptors, true); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's request. The given {@code attributes} will be available during snippet - * generation and the cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. - * @param attributes the attributes - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet requestCookies(Map attributes, - CookieDescriptor... descriptors) { - return requestCookies(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's request. The given {@code attributes} will be available during snippet - * generation and the cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. - * @param attributes the attributes - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet requestCookies(Map attributes, - List descriptors) { - return new RequestCookiesSnippet(descriptors, attributes); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's request. The given {@code attributes} will be available during snippet - * generation and the cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented cookies will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet relaxedRequestCookies(Map attributes, - CookieDescriptor... descriptors) { - return relaxedRequestCookies(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's request. The given {@code attributes} will be available during snippet - * generation and the cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented cookies will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's cookies - * @return the snippet that will document the request cookies - * @see #cookieWithName(String) - */ - public static RequestCookiesSnippet relaxedRequestCookies(Map attributes, - List descriptors) { - return new RequestCookiesSnippet(descriptors, attributes, true); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * response. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the - * response, a failure will also occur. - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet responseCookies(CookieDescriptor... descriptors) { - return responseCookies(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * response. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the - * response, a failure will also occur. - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet responseCookies(List descriptors) { - return new ResponseCookiesSnippet(descriptors); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * response. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented cookies will be ignored. - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet relaxedResponseCookies(CookieDescriptor... descriptors) { - return relaxedResponseCookies(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API operation's - * response. The cookies will be documented using the given {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented cookies will be ignored. - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet relaxedResponseCookies(List descriptors) { - return new ResponseCookiesSnippet(descriptors, true); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's response. The given {@code attributes} will be available during - * snippet generation and the cookies will be documented using the given - * {@code descriptors}. - *

- * If a cookie is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the - * response, a failure will also occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet responseCookies(Map attributes, - CookieDescriptor... descriptors) { - return responseCookies(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's response. The given {@code attributes} will be available during - * snippet generation and the cookies will be documented using the given - * {@code descriptors}. - *

- * If a cookie is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * cookie is documented, is not marked as optional, and is not present in the - * response, a failure will also occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet responseCookies(Map attributes, - List descriptors) { - return new ResponseCookiesSnippet(descriptors, attributes); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's response. The given {@code attributes} will be available during - * snippet generation and the cookies will be documented using the given - * {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented cookies will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet relaxedResponseCookies(Map attributes, - CookieDescriptor... descriptors) { - return relaxedResponseCookies(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the cookies of the API - * operations's response. The given {@code attributes} will be available during - * snippet generation and the cookies will be documented using the given - * {@code descriptors}. - *

- * If a cookie is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented cookies will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the response's cookies - * @return the snippet that will document the response cookies - * @see #cookieWithName(String) - */ - public static ResponseCookiesSnippet relaxedResponseCookies(Map attributes, - List descriptors) { - return new ResponseCookiesSnippet(descriptors, attributes, true); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/RequestCookiesSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/RequestCookiesSnippet.java deleted file mode 100644 index e5f4d5d2a..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/RequestCookiesSnippet.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; - -/** - * A {@link Snippet} that documents the cookies in a request. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - * @since 3.0 - * @see CookieDocumentation#requestCookies(CookieDescriptor...) - * @see CookieDocumentation#requestCookies(Map, CookieDescriptor...) - */ -public class RequestCookiesSnippet extends AbstractCookiesSnippet { - - /** - * Creates a new {@code RequestCookiesSnippet} that will document the cookies in the - * request using the given {@code descriptors}. - * @param descriptors the descriptors - */ - protected RequestCookiesSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code RequestCookiesSnippet} that will document the cookies in the - * request using the given {@code descriptors}. If {@code ignoreUndocumentedCookies} - * is {@code true}, undocumented cookies will be ignored and will not trigger a - * failure. - * @param descriptors the descriptors - * @param ignoreUndocumentedCookies whether undocumented cookies should be ignored - */ - protected RequestCookiesSnippet(List descriptors, boolean ignoreUndocumentedCookies) { - this(descriptors, null, ignoreUndocumentedCookies); - } - - /** - * Creates a new {@code RequestCookiesSnippet} that will document the cookies in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. Undocumented cookies will not be - * ignored. - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected RequestCookiesSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code RequestCookiesSnippet} that will document the cookies in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedCookies whether undocumented cookies should be ignored - */ - protected RequestCookiesSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedCookies) { - super("request", descriptors, attributes, ignoreUndocumentedCookies); - } - - @Override - protected Set extractActualCookies(Operation operation) { - HashSet actualCookies = new HashSet<>(); - for (RequestCookie cookie : operation.getRequest().getCookies()) { - actualCookies.add(cookie.getName()); - } - return actualCookies; - } - - @Override - protected void verificationFailed(Set undocumentedCookies, Set missingCookies) { - String message = ""; - if (!undocumentedCookies.isEmpty()) { - message += "Cookies with the following names were not documented: " + undocumentedCookies; - } - if (!missingCookies.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Cookies with the following names were not found in the request: " + missingCookies; - } - throw new SnippetException(message); - } - - /** - * Returns a new {@code RequestCookiesSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestCookiesSnippet and(CookieDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code RequestCookiesSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestCookiesSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(this.getCookieDescriptors().values()); - combinedDescriptors.addAll(additionalDescriptors); - return new RequestCookiesSnippet(combinedDescriptors, getAttributes(), isIgnoreUndocumentedCookies()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/ResponseCookiesSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/ResponseCookiesSnippet.java deleted file mode 100644 index f6a5176f9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/ResponseCookiesSnippet.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.ResponseCookie; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; - -/** - * A {@link Snippet} that documents the cookies in a response. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - * @since 3.0 - * @see CookieDocumentation#responseCookies(CookieDescriptor...) - * @see CookieDocumentation#responseCookies(Map, CookieDescriptor...) - */ -public class ResponseCookiesSnippet extends AbstractCookiesSnippet { - - /** - * Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the - * response using the given {@code descriptors}. - * @param descriptors the descriptors - */ - protected ResponseCookiesSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the - * response using the given {@code descriptors}. If {@code ignoreUndocumentedCookies} - * is {@code true}, undocumented cookies will be ignored and will not trigger a - * failure. - * @param descriptors the descriptors - * @param ignoreUndocumentedCookies whether undocumented cookies should be ignored - */ - protected ResponseCookiesSnippet(List descriptors, boolean ignoreUndocumentedCookies) { - this(descriptors, null, ignoreUndocumentedCookies); - } - - /** - * Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the - * response using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. Undocumented cookies will not be - * ignored. - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected ResponseCookiesSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code ResponseCookiesSnippet} that will document the cookies in the - * response using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedCookies whether undocumented cookies should be ignored - */ - protected ResponseCookiesSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedCookies) { - super("response", descriptors, attributes, ignoreUndocumentedCookies); - } - - @Override - protected Set extractActualCookies(Operation operation) { - return operation.getResponse().getCookies().stream().map(ResponseCookie::getName).collect(Collectors.toSet()); - } - - @Override - protected void verificationFailed(Set undocumentedCookies, Set missingCookies) { - String message = ""; - if (!undocumentedCookies.isEmpty()) { - message += "Cookies with the following names were not documented: " + undocumentedCookies; - } - if (!missingCookies.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Cookies with the following names were not found in the response: " + missingCookies; - } - throw new SnippetException(message); - } - - /** - * Returns a new {@code ResponseCookiesSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseCookiesSnippet and(CookieDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code ResponseCookiesSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseCookiesSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(this.getCookieDescriptors().values()); - combinedDescriptors.addAll(additionalDescriptors); - return new ResponseCookiesSnippet(combinedDescriptors, getAttributes(), isIgnoreUndocumentedCookies()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/package-info.java deleted file mode 100644 index afc01d60f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cookies/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting the cookies of a RESTful API's requests and responses. - */ -package org.springframework.restdocs.cookies; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerationException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerationException.java deleted file mode 100644 index b12a1a9cd..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerationException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.generate; - -/** - * An exception that can be thrown when a failure occurs during REST documentation - * generation. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public class RestDocumentationGenerationException extends RuntimeException { - - /** - * Creates a new {@code RestDocumentationException} with the given {@code cause}. - * @param cause the cause - */ - public RestDocumentationGenerationException(Throwable cause) { - super(cause); - } - - /** - * Creates a new {@code RestDocumentationException} with the given {@code message} and - * {@code cause}. - * @param message the message - * @param cause the cause - */ - public RestDocumentationGenerationException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerator.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerator.java deleted file mode 100644 index 04e64bbb9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerator.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.generate; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.RequestConverter; -import org.springframework.restdocs.operation.ResponseConverter; -import org.springframework.restdocs.operation.StandardOperation; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.util.Assert; - -/** - * A {@code RestDocumentationGenerator} is used to generate documentation snippets from - * the request and response of an operation performed on a service. - * - * @param the request type that can be handled - * @param the response type that can be handled - * @author Andy Wilkinson - * @author Filip Hrisafov - * @since 1.1.0 - */ -public final class RestDocumentationGenerator { - - /** - * Name of the operation attribute used to hold the request's URL template. - */ - public static final String ATTRIBUTE_NAME_URL_TEMPLATE = "org.springframework.restdocs.urlTemplate"; - - /** - * Name of the operation attribute used to hold the {@link List} of default snippets. - */ - public static final String ATTRIBUTE_NAME_DEFAULT_SNIPPETS = "org.springframework.restdocs.defaultSnippets"; - - /** - * Name of the operation attribute used to hold the default operation request - * preprocessor. - */ - public static final String ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR = "org.springframework.restdocs.defaultOperationRequestPreprocessor"; - - /** - * Name of the operation attribute used to hold the default operation response - * preprocessor. - */ - public static final String ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR = "org.springframework.restdocs.defaultOperationResponsePreprocessor"; - - private final String identifier; - - private final OperationRequestPreprocessor requestPreprocessor; - - private final OperationResponsePreprocessor responsePreprocessor; - - private final List snippets; - - private final RequestConverter requestConverter; - - private final ResponseConverter responseConverter; - - /** - * Creates a new {@code RestDocumentationGenerator} for the operation identified by - * the given {@code identifier}. The given {@code requestConverter} and - * {@code responseConverter} are used to convert the operation's request and response - * into generic {@code OperationRequest} and {@code OperationResponse} instances that - * can then be documented. The given documentation {@code snippets} will be produced. - * @param identifier the identifier for the operation - * @param requestConverter the request converter - * @param responseConverter the response converter - * @param snippets the snippets - */ - public RestDocumentationGenerator(String identifier, RequestConverter requestConverter, - ResponseConverter responseConverter, Snippet... snippets) { - this(identifier, requestConverter, responseConverter, new IdentityOperationRequestPreprocessor(), - new IdentityOperationResponsePreprocessor(), snippets); - } - - /** - * Creates a new {@code RestDocumentationGenerator} for the operation identified by - * the given {@code identifier}. The given {@code requestConverter} and - * {@code responseConverter} are used to convert the operation's request and response - * into generic {@code OperationRequest} and {@code OperationResponse} instances that - * can then be documented. The given {@code requestPreprocessor} is applied to the - * request before it is documented. The given documentation {@code snippets} will be - * produced. - * @param identifier the identifier for the operation - * @param requestConverter the request converter - * @param responseConverter the response converter - * @param requestPreprocessor the request preprocessor - * @param snippets the snippets - */ - public RestDocumentationGenerator(String identifier, RequestConverter requestConverter, - ResponseConverter responseConverter, OperationRequestPreprocessor requestPreprocessor, - Snippet... snippets) { - this(identifier, requestConverter, responseConverter, requestPreprocessor, - new IdentityOperationResponsePreprocessor(), snippets); - } - - /** - * Creates a new {@code RestDocumentationGenerator} for the operation identified by - * the given {@code identifier}. The given {@code requestConverter} and - * {@code responseConverter} are used to convert the operation's request and response - * into generic {@code OperationRequest} and {@code OperationResponse} instances that - * can then be documented. The given {@code responsePreprocessor} is applied to the - * response before it is documented. The given documentation {@code snippets} will be - * produced. - * @param identifier the identifier for the operation - * @param requestConverter the request converter - * @param responseConverter the response converter - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - */ - public RestDocumentationGenerator(String identifier, RequestConverter requestConverter, - ResponseConverter responseConverter, OperationResponsePreprocessor responsePreprocessor, - Snippet... snippets) { - this(identifier, requestConverter, responseConverter, new IdentityOperationRequestPreprocessor(), - responsePreprocessor, snippets); - } - - /** - * Creates a new {@code RestDocumentationGenerator} for the operation identified by - * the given {@code identifier}. The given {@code requestConverter} and - * {@code responseConverter} are used to convert the operation's request and response - * into generic {@code OperationRequest} and {@code OperationResponse} instances that - * can then be documented. The given {@code requestPreprocessor} and - * {@code responsePreprocessor} are applied to the request and response before they - * are documented. The given documentation {@code snippets} will be produced. - * @param identifier the identifier for the operation - * @param requestConverter the request converter - * @param responseConverter the response converter - * @param requestPreprocessor the request preprocessor - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - */ - public RestDocumentationGenerator(String identifier, RequestConverter requestConverter, - ResponseConverter responseConverter, OperationRequestPreprocessor requestPreprocessor, - OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - Assert.notNull(identifier, "identifier must be non-null"); - Assert.notNull(requestConverter, "requestConverter must be non-null"); - Assert.notNull(responseConverter, "responseConverter must be non-null"); - Assert.notNull(identifier, "identifier must be non-null"); - Assert.notNull(requestPreprocessor, "requestPreprocessor must be non-null"); - Assert.notNull(responsePreprocessor, "responsePreprocessor must be non-null"); - Assert.notNull(snippets, "snippets must be non-null"); - this.identifier = identifier; - this.requestConverter = requestConverter; - this.responseConverter = responseConverter; - this.requestPreprocessor = requestPreprocessor; - this.responsePreprocessor = responsePreprocessor; - this.snippets = new ArrayList<>(Arrays.asList(snippets)); - } - - /** - * Handles the given {@code request} and {@code response}, producing documentation - * snippets for them using the given {@code configuration}. - * @param request the request - * @param response the request - * @param configuration the configuration - * @throws RestDocumentationGenerationException if a failure occurs during handling - */ - public void handle(REQ request, RESP response, Map configuration) { - Map attributes = new HashMap<>(configuration); - OperationRequest operationRequest = preprocessRequest(this.requestConverter.convert(request), attributes); - OperationResponse operationResponse = preprocessResponse(this.responseConverter.convert(response), attributes); - Operation operation = new StandardOperation(this.identifier, operationRequest, operationResponse, attributes); - try { - for (Snippet snippet : getSnippets(attributes)) { - snippet.document(operation); - } - } - catch (IOException ex) { - throw new RestDocumentationGenerationException(ex); - } - } - - /** - * Creates a new {@link RestDocumentationGenerator} with the same configuration as - * this one other than its snippets. The new generator will use the given - * {@code snippets}. - * @param snippets the snippets - * @return the new generator - */ - public RestDocumentationGenerator withSnippets(Snippet... snippets) { - return new RestDocumentationGenerator<>(this.identifier, this.requestConverter, this.responseConverter, - this.requestPreprocessor, this.responsePreprocessor, snippets); - } - - @SuppressWarnings("unchecked") - private List getSnippets(Map configuration) { - List combinedSnippets = new ArrayList<>(); - List defaultSnippets = (List) configuration.get(ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - if (defaultSnippets != null) { - combinedSnippets.addAll(defaultSnippets); - } - combinedSnippets.addAll(this.snippets); - return combinedSnippets; - } - - private OperationRequest preprocessRequest(OperationRequest request, Map configuration) { - return preprocess(getRequestPreprocessors(configuration), request, this::preprocess); - } - - private OperationRequest preprocess(OperationRequestPreprocessor preprocessor, OperationRequest request) { - return preprocessor.preprocess(request); - } - - private List getRequestPreprocessors(Map configuration) { - return getPreprocessors(this.requestPreprocessor, - RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR, configuration); - } - - private OperationResponse preprocessResponse(OperationResponse response, Map configuration) { - return preprocess(getResponsePreprocessors(configuration), response, this::preprocess); - } - - private OperationResponse preprocess(OperationResponsePreprocessor preprocessor, OperationResponse response) { - return preprocessor.preprocess(response); - } - - private List getResponsePreprocessors(Map configuration) { - return getPreprocessors(this.responsePreprocessor, - RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR, configuration); - } - - private T preprocess(List

preprocessors, T target, BiFunction function) { - T processed = target; - for (P preprocessor : preprocessors) { - processed = function.apply(preprocessor, processed); - } - return processed; - } - - @SuppressWarnings("unchecked") - private List getPreprocessors(T preprocessor, String preprocessorAttribute, - Map configuration) { - List preprocessors = new ArrayList<>(2); - preprocessors.add(preprocessor); - T defaultResponsePreprocessor = (T) configuration.get(preprocessorAttribute); - if (defaultResponsePreprocessor != null) { - preprocessors.add(defaultResponsePreprocessor); - } - return preprocessors; - } - - private static final class IdentityOperationRequestPreprocessor implements OperationRequestPreprocessor { - - @Override - public OperationRequest preprocess(OperationRequest request) { - return request; - } - - } - - private static final class IdentityOperationResponsePreprocessor implements OperationResponsePreprocessor { - - @Override - public OperationResponse preprocess(OperationResponse response) { - return response; - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/package-info.java deleted file mode 100644 index a0d745526..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Classes that drive the generation of the documentation snippets. - */ -package org.springframework.restdocs.generate; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/AbstractHeadersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/AbstractHeadersSnippet.java deleted file mode 100644 index 6e049b7a7..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/AbstractHeadersSnippet.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; - -/** - * Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that - * document a RESTful resource's request or response headers. - * - * @author Andreas Evers - */ -public abstract class AbstractHeadersSnippet extends TemplatedSnippet { - - private List headerDescriptors; - - private String type; - - /** - * Creates a new {@code AbstractHeadersSnippet} that will produce a snippet named - * {@code -headers}. The headers will be documented using the given - * {@code descriptors} and the given {@code attributes} will be included in the model - * during template rendering. - * @param type the type of the headers - * @param descriptors the header descriptors - * @param attributes the additional attributes - */ - protected AbstractHeadersSnippet(String type, List descriptors, Map attributes) { - super(type + "-headers", attributes); - for (HeaderDescriptor descriptor : descriptors) { - Assert.notNull(descriptor.getName(), "The name of the header must not be null"); - Assert.notNull(descriptor.getDescription(), "The description of the header must not be null"); - } - this.headerDescriptors = descriptors; - this.type = type; - } - - @Override - protected Map createModel(Operation operation) { - validateHeaderDocumentation(operation); - - Map model = new HashMap<>(); - List> headers = new ArrayList<>(); - model.put("headers", headers); - for (HeaderDescriptor descriptor : this.headerDescriptors) { - headers.add(createModelForDescriptor(descriptor)); - } - return model; - } - - private void validateHeaderDocumentation(Operation operation) { - List missingHeaders = findMissingHeaders(operation); - if (!missingHeaders.isEmpty()) { - List names = new ArrayList<>(); - for (HeaderDescriptor headerDescriptor : missingHeaders) { - names.add(headerDescriptor.getName()); - } - throw new SnippetException( - "Headers with the following names were not found" + " in the " + this.type + ": " + names); - } - } - - /** - * Finds the headers that are missing from the operation. A header is missing if it is - * described by one of the {@code headerDescriptors} but is not present in the - * operation. - * @param operation the operation - * @return descriptors for the headers that are missing from the operation - */ - protected List findMissingHeaders(Operation operation) { - List missingHeaders = new ArrayList<>(); - Set actualHeaders = extractActualHeaders(operation); - for (HeaderDescriptor headerDescriptor : this.headerDescriptors) { - if (!headerDescriptor.isOptional() && !actualHeaders.contains(headerDescriptor.getName())) { - missingHeaders.add(headerDescriptor); - } - } - - return missingHeaders; - } - - /** - * Extracts the names of the headers from the request or response of the given - * {@code operation}. - * @param operation the operation - * @return the header names - */ - protected abstract Set extractActualHeaders(Operation operation); - - /** - * Returns the list of {@link HeaderDescriptor HeaderDescriptors} that will be used to - * generate the documentation. - * @return the header descriptors - */ - protected final List getHeaderDescriptors() { - return this.headerDescriptors; - } - - /** - * Returns a model for the given {@code descriptor}. - * @param descriptor the descriptor - * @return the model - */ - protected Map createModelForDescriptor(HeaderDescriptor descriptor) { - Map model = new HashMap<>(); - model.put("name", descriptor.getName()); - model.put("description", descriptor.getDescription()); - model.put("optional", descriptor.isOptional()); - model.putAll(descriptor.getAttributes()); - return model; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/HeaderDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/HeaderDescriptor.java deleted file mode 100644 index 121446239..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/HeaderDescriptor.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import org.springframework.http.HttpHeaders; -import org.springframework.restdocs.snippet.AbstractDescriptor; - -/** - * A description of a header found in a request or response. - * - * @author Andreas Evers - * @see HeaderDocumentation#headerWithName(String) - */ -public class HeaderDescriptor extends AbstractDescriptor { - - private final String name; - - private boolean optional; - - /** - * Creates a new {@code HeaderDescriptor} describing the header with the given - * {@code name}. - * @param name the name - * @see HttpHeaders - */ - protected HeaderDescriptor(String name) { - this.name = name; - } - - /** - * Marks the header as optional. - * @return {@code this} - */ - public final HeaderDescriptor optional() { - this.optional = true; - return this; - } - - /** - * Returns the name for the header. - * @return the header name - */ - public final String getName() { - return this.name; - } - - /** - * Returns {@code true} if the described header is optional, otherwise {@code false}. - * @return {@code true} if the described header is optional, otherwise {@code false} - */ - public final boolean isOptional() { - return this.optional; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/HeaderDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/HeaderDocumentation.java deleted file mode 100644 index e3a090f0c..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/HeaderDocumentation.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.snippet.Snippet; - -/** - * Static factory methods for documenting a RESTful API's request and response headers. - * - * @author Andreas Evers - * @author Andy Wilkinson - * @author Marcel Overdijk - */ -public abstract class HeaderDocumentation { - - private HeaderDocumentation() { - - } - - /** - * Creates a {@code HeaderDescriptor} that describes a header with the given - * {@code name}. - * @param name the name of the header - * @return a {@code HeaderDescriptor} ready for further configuration - */ - public static HeaderDescriptor headerWithName(String name) { - return new HeaderDescriptor(name); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API operation's - * request. The headers will be documented using the given {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * request, a failure will occur. - * @param descriptors the descriptions of the request's headers - * @return the snippet that will document the request headers - * @see #headerWithName(String) - */ - public static RequestHeadersSnippet requestHeaders(HeaderDescriptor... descriptors) { - return requestHeaders(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API operation's - * request. The headers will be documented using the given {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * request, a failure will occur. - * @param descriptors the descriptions of the request's headers - * @return the snippet that will document the request headers - * @see #headerWithName(String) - */ - public static RequestHeadersSnippet requestHeaders(List descriptors) { - return new RequestHeadersSnippet(descriptors); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API - * operations's request. The given {@code attributes} will be available during snippet - * generation and the headers will be documented using the given {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * request, a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the request's headers - * @return the snippet that will document the request headers - * @see #headerWithName(String) - */ - public static RequestHeadersSnippet requestHeaders(Map attributes, - HeaderDescriptor... descriptors) { - return requestHeaders(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API - * operations's request. The given {@code attributes} will be available during snippet - * generation and the headers will be documented using the given {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * request, a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the request's headers - * @return the snippet that will document the request headers - * @see #headerWithName(String) - */ - public static RequestHeadersSnippet requestHeaders(Map attributes, - List descriptors) { - return new RequestHeadersSnippet(descriptors, attributes); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API operation's - * response. The headers will be documented using the given {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * request, a failure will occur. - * @param descriptors the descriptions of the response's headers - * @return the snippet that will document the response headers - * @see #headerWithName(String) - */ - public static ResponseHeadersSnippet responseHeaders(HeaderDescriptor... descriptors) { - return responseHeaders(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API operation's - * response. The headers will be documented using the given {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * request, a failure will occur. - * @param descriptors the descriptions of the response's headers - * @return the snippet that will document the response headers - * @see #headerWithName(String) - */ - public static ResponseHeadersSnippet responseHeaders(List descriptors) { - return new ResponseHeadersSnippet(descriptors); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API - * operations's response. The given {@code attributes} will be available during - * snippet generation and the headers will be documented using the given - * {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * response, a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's headers - * @return the snippet that will document the response headers - * @see #headerWithName(String) - */ - public static ResponseHeadersSnippet responseHeaders(Map attributes, - HeaderDescriptor... descriptors) { - return responseHeaders(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@link Snippet} that will document the headers of the API - * operations's response. The given {@code attributes} will be available during - * snippet generation and the headers will be documented using the given - * {@code descriptors}. - *

- * If a header is documented, is not marked as optional, and is not present in the - * response, a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's headers - * @return the snippet that will document the response headers - * @see #headerWithName(String) - */ - public static ResponseHeadersSnippet responseHeaders(Map attributes, - List descriptors) { - return new ResponseHeadersSnippet(descriptors, attributes); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java deleted file mode 100644 index 79d587928..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/RequestHeadersSnippet.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - -/** - * A {@link Snippet} that documents the headers in a request. - * - * @author Andreas Evers - * @author Andy Wilkinson - * @see HeaderDocumentation#requestHeaders(HeaderDescriptor...) - * @see HeaderDocumentation#requestHeaders(Map, HeaderDescriptor...) - */ -public class RequestHeadersSnippet extends AbstractHeadersSnippet { - - /** - * Creates a new {@code RequestHeadersSnippet} that will document the headers in the - * request using the given {@code descriptors}. - * @param descriptors the descriptors - */ - protected RequestHeadersSnippet(List descriptors) { - this(descriptors, null); - } - - /** - * Creates a new {@code RequestHeadersSnippet} that will document the headers in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected RequestHeadersSnippet(List descriptors, Map attributes) { - super("request", descriptors, attributes); - } - - @Override - protected Set extractActualHeaders(Operation operation) { - return operation.getRequest().getHeaders().headerNames(); - } - - /** - * Returns a new {@code RequestHeadersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestHeadersSnippet and(HeaderDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code RequestHeadersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestHeadersSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(this.getHeaderDescriptors()); - combinedDescriptors.addAll(additionalDescriptors); - return new RequestHeadersSnippet(combinedDescriptors, getAttributes()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java deleted file mode 100644 index ea9687e52..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/ResponseHeadersSnippet.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - -/** - * A {@link Snippet} that documents the headers in a response. - * - * @author Andreas Evers - * @author Andy Wilkinson - * @see HeaderDocumentation#responseHeaders(HeaderDescriptor...) - * @see HeaderDocumentation#responseHeaders(Map, HeaderDescriptor...) - */ -public class ResponseHeadersSnippet extends AbstractHeadersSnippet { - - /** - * Creates a new {@code ResponseHeadersSnippet} that will document the headers in the - * response using the given {@code descriptors}. - * @param descriptors the descriptors - */ - protected ResponseHeadersSnippet(List descriptors) { - this(descriptors, null); - } - - /** - * Creates a new {@code ResponseHeadersSnippet} that will document the headers in the - * response using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected ResponseHeadersSnippet(List descriptors, Map attributes) { - super("response", descriptors, attributes); - } - - @Override - protected Set extractActualHeaders(Operation operation) { - return operation.getResponse().getHeaders().headerNames(); - } - - /** - * Returns a new {@code ResponseHeadersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseHeadersSnippet and(HeaderDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code ResponseHeadersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseHeadersSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(this.getHeaderDescriptors()); - combinedDescriptors.addAll(additionalDescriptors); - return new ResponseHeadersSnippet(combinedDescriptors, getAttributes()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/package-info.java deleted file mode 100644 index bbc63a954..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/headers/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting the headers of a RESTful API's requests and responses. - */ -package org.springframework.restdocs.headers; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java deleted file mode 100644 index 3fac7ffac..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpDocumentation.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.http; - -import java.util.Map; - -/** - * Static factory methods for documenting a RESTful API's HTTP requests. - * - * @author Andy Wilkinson - * @author Jonathan Pearlin - */ -public abstract class HttpDocumentation { - - private HttpDocumentation() { - - } - - /** - * Returns a new {@code Snippet} that will document the HTTP request for the API - * operation. - * @return the snippet that will document the HTTP request - */ - public static HttpRequestSnippet httpRequest() { - return new HttpRequestSnippet(); - } - - /** - * Returns a new {@code Snippet} that will document the HTTP request for the API - * operation. The given {@code attributes} will be available during snippet - * generation. - * @param attributes the attributes - * @return the snippet that will document the HTTP request - */ - public static HttpRequestSnippet httpRequest(Map attributes) { - return new HttpRequestSnippet(attributes); - } - - /** - * Returns a {@code Snippet} that will document the HTTP response for the API - * operation. - * @return the snippet that will document the HTTP response - */ - public static HttpResponseSnippet httpResponse() { - return new HttpResponseSnippet(); - } - - /** - * Returns a {@code Snippet} that will document the HTTP response for the API - * operation. The given {@code attributes} will be available during snippet - * generation. - * @param attributes the attributes - * @return the snippet that will document the HTTP response - */ - public static HttpResponseSnippet httpResponse(Map attributes) { - return new HttpResponseSnippet(attributes); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java deleted file mode 100644 index 87087b315..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.http; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.StringUtils; - -/** - * A {@link Snippet} that documents an HTTP request. - * - * @author Andy Wilkinson - * @see HttpDocumentation#httpRequest() - * @see HttpDocumentation#httpRequest(Map) - */ -public class HttpRequestSnippet extends TemplatedSnippet { - - private static final String MULTIPART_BOUNDARY = "6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm"; - - /** - * Creates a new {@code HttpRequestSnippet} with no additional attributes. - */ - protected HttpRequestSnippet() { - this(null); - } - - /** - * Creates a new {@code HttpRequestSnippet} with the given additional - * {@code attributes} that will be included in the model during template rendering. - * @param attributes the additional attributes - */ - protected HttpRequestSnippet(Map attributes) { - super("http-request", attributes); - } - - @Override - protected Map createModel(Operation operation) { - Map model = new HashMap<>(); - model.put("method", operation.getRequest().getMethod()); - model.put("path", getPath(operation.getRequest())); - model.put("headers", getHeaders(operation.getRequest())); - model.put("requestBody", getRequestBody(operation.getRequest())); - return model; - } - - private String getPath(OperationRequest request) { - String path = request.getUri().getRawPath(); - String queryString = request.getUri().getRawQuery(); - if (StringUtils.hasText(queryString)) { - path = path + "?" + queryString; - } - return path; - } - - private boolean includeParametersInUri(OperationRequest request) { - HttpMethod method = request.getMethod(); - return (method != HttpMethod.PUT && method != HttpMethod.POST && method != HttpMethod.PATCH) - || (request.getContent().length > 0 && !MediaType.APPLICATION_FORM_URLENCODED - .isCompatibleWith(request.getHeaders().getContentType())); - } - - private List> getHeaders(OperationRequest request) { - List> headers = new ArrayList<>(); - - for (Entry> header : request.getHeaders().headerSet()) { - for (String value : header.getValue()) { - if (HttpHeaders.CONTENT_TYPE.equals(header.getKey()) && !request.getParts().isEmpty()) { - headers.add(header(header.getKey(), String.format("%s; boundary=%s", value, MULTIPART_BOUNDARY))); - } - else { - headers.add(header(header.getKey(), value)); - } - - } - } - - List cookies = new ArrayList<>(); - for (RequestCookie cookie : request.getCookies()) { - cookies.add(String.format("%s=%s", cookie.getName(), cookie.getValue())); - } - if (!cookies.isEmpty()) { - headers.add(header(HttpHeaders.COOKIE, String.join("; ", cookies))); - } - - if (requiresFormEncodingContentTypeHeader(request)) { - headers.add(header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)); - } - return headers; - } - - private String getRequestBody(OperationRequest request) { - StringWriter httpRequest = new StringWriter(); - PrintWriter writer = new PrintWriter(httpRequest); - String content = request.getContentAsString(); - if (StringUtils.hasText(content)) { - writer.printf("%n%s", content); - } - else if (isPutPostOrPatch(request)) { - if (!request.getParts().isEmpty()) { - writeParts(request, writer); - } - } - return httpRequest.toString(); - } - - private boolean isPutPostOrPatch(OperationRequest request) { - return HttpMethod.PUT.equals(request.getMethod()) || HttpMethod.POST.equals(request.getMethod()) - || HttpMethod.PATCH.equals(request.getMethod()); - } - - private void writeParts(OperationRequest request, PrintWriter writer) { - writer.println(); - for (OperationRequestPart part : request.getParts()) { - writePartBoundary(writer); - writePart(part, writer); - writer.println(); - } - writeMultipartEnd(writer); - } - - private void writePartBoundary(PrintWriter writer) { - writer.printf("--%s%n", MULTIPART_BOUNDARY); - } - - private void writePart(OperationRequestPart part, PrintWriter writer) { - writePart(part.getName(), part.getContentAsString(), part.getSubmittedFileName(), - part.getHeaders().getContentType(), writer); - } - - private void writePart(String name, String value, String filename, MediaType contentType, PrintWriter writer) { - writer.printf("Content-Disposition: form-data; name=%s", name); - if (StringUtils.hasText(filename)) { - writer.printf("; filename=%s", filename); - } - writer.printf("%n"); - if (contentType != null) { - writer.printf("Content-Type: %s%n", contentType); - } - writer.println(); - writer.print(value); - } - - private void writeMultipartEnd(PrintWriter writer) { - writer.printf("--%s--", MULTIPART_BOUNDARY); - } - - private boolean requiresFormEncodingContentTypeHeader(OperationRequest request) { - return request.getHeaders().get(HttpHeaders.CONTENT_TYPE) == null && isPutPostOrPatch(request) - && !includeParametersInUri(request); - } - - private Map header(String name, String value) { - Map header = new HashMap<>(); - header.put("name", name); - header.put("value", value); - return header; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java deleted file mode 100644 index 919d312f9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpResponseSnippet.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.http; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.TemplatedSnippet; - -/** - * A {@link Snippet} that documents an HTTP response. - * - * @author Andy Wilkinson - * @see HttpDocumentation#httpResponse() - * @see HttpDocumentation#httpResponse(Map) - */ -public class HttpResponseSnippet extends TemplatedSnippet { - - /** - * Creates a new {@code HttpResponseSnippet} with no additional attributes. - */ - protected HttpResponseSnippet() { - this(null); - } - - /** - * Creates a new {@code HttpResponseSnippet} with the given additional - * {@code attributes} that will be included in the model during template rendering. - * @param attributes the additional attributes - */ - protected HttpResponseSnippet(Map attributes) { - super("http-response", attributes); - } - - @Override - protected Map createModel(Operation operation) { - OperationResponse response = operation.getResponse(); - Map model = new HashMap<>(); - model.put("responseBody", responseBody(response)); - model.put("headers", headers(response)); - HttpStatusCode status = response.getStatus(); - model.put("statusCode", status.value()); - model.put("statusReason", (status instanceof HttpStatus) ? ((HttpStatus) status).getReasonPhrase() : ""); - return model; - } - - private String responseBody(OperationResponse response) { - String content = response.getContentAsString(); - return content.isEmpty() ? content : String.format("%n%s", content); - } - - private List> headers(OperationResponse response) { - List> headers = new ArrayList<>(); - for (Entry> header : response.getHeaders().headerSet()) { - List values = header.getValue(); - for (String value : values) { - headers.add(header(header.getKey(), value)); - } - } - return headers; - } - - private Map header(String name, String value) { - Map header = new HashMap<>(); - header.put("name", name); - header.put("value", value); - return header; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/package-info.java deleted file mode 100644 index c2f8d4f53..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting the HTTP request sent to a RESTful API and the HTTP response that is - * returned. - */ -package org.springframework.restdocs.http; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/AbstractJsonLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/AbstractJsonLinkExtractor.java deleted file mode 100644 index a019ef7e7..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/AbstractJsonLinkExtractor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.restdocs.operation.OperationResponse; - -/** - * Abstract base class for a {@link LinkExtractor} that extracts links from JSON. - * - * @author Andy Wilkinson - */ -abstract class AbstractJsonLinkExtractor implements LinkExtractor { - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Override - @SuppressWarnings("unchecked") - public Map> extractLinks(OperationResponse response) throws IOException { - Map jsonContent = this.objectMapper.readValue(response.getContent(), Map.class); - return extractLinks(jsonContent); - } - - protected abstract Map> extractLinks(Map json); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/AtomLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/AtomLinkExtractor.java deleted file mode 100644 index f2a68f897..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/AtomLinkExtractor.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * {@link LinkExtractor} that extracts links in Atom format. - * - * @author Andy Wilkinson - */ -@SuppressWarnings("unchecked") -class AtomLinkExtractor extends AbstractJsonLinkExtractor { - - @Override - public Map> extractLinks(Map json) { - MultiValueMap extractedLinks = new LinkedMultiValueMap<>(); - Object possibleLinks = json.get("links"); - if (possibleLinks instanceof Collection) { - Collection linksCollection = (Collection) possibleLinks; - for (Object linkObject : linksCollection) { - if (linkObject instanceof Map) { - Link link = maybeCreateLink((Map) linkObject); - maybeStoreLink(link, extractedLinks); - } - } - } - return extractedLinks; - } - - private static Link maybeCreateLink(Map linkMap) { - Object hrefObject = linkMap.get("href"); - Object relObject = linkMap.get("rel"); - if (relObject instanceof String && hrefObject instanceof String) { - Object titleObject = linkMap.get("title"); - return new Link((String) relObject, (String) hrefObject, - (titleObject instanceof String) ? (String) titleObject : null); - } - return null; - } - - private static void maybeStoreLink(Link link, MultiValueMap extractedLinks) { - if (link != null) { - extractedLinks.add(link.getRel(), link); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java deleted file mode 100644 index bdf5772cb..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationResponse; - -/** - * {@link LinkExtractor} that delegates to other link extractors based on the response's - * content type. - * - * @author Andy Wilkinson - * @author Oliver Drotbohm - */ -class ContentTypeLinkExtractor implements LinkExtractor { - - private Map linkExtractors = new HashMap<>(); - - ContentTypeLinkExtractor() { - this.linkExtractors.put(MediaType.APPLICATION_JSON, new AtomLinkExtractor()); - LinkExtractor halLinkExtractor = new HalLinkExtractor(); - this.linkExtractors.put(HalLinkExtractor.HAL_MEDIA_TYPE, halLinkExtractor); - this.linkExtractors.put(HalLinkExtractor.VND_HAL_MEDIA_TYPE, halLinkExtractor); - this.linkExtractors.put(HalLinkExtractor.HAL_FORMS_MEDIA_TYPE, halLinkExtractor); - } - - ContentTypeLinkExtractor(Map linkExtractors) { - this.linkExtractors.putAll(linkExtractors); - } - - @Override - public Map> extractLinks(OperationResponse response) throws IOException { - MediaType contentType = response.getHeaders().getContentType(); - LinkExtractor extractorForContentType = getExtractorForContentType(contentType); - if (extractorForContentType != null) { - return extractorForContentType.extractLinks(response); - } - throw new IllegalStateException( - "No LinkExtractor has been provided and one is not available for the " + "content type " + contentType); - } - - private LinkExtractor getExtractorForContentType(MediaType contentType) { - if (contentType != null) { - for (Entry entry : this.linkExtractors.entrySet()) { - if (contentType.isCompatibleWith(entry.getKey())) { - return entry.getValue(); - } - } - } - return null; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java deleted file mode 100644 index e56f32ba8..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HalLinkExtractor.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.MediaType; - -/** - * {@link LinkExtractor} that extracts links in Hypermedia Application Language (HAL) - * format. - * - * @author Andy Wilkinson - * @author Oliver Drotbohm - */ -class HalLinkExtractor extends AbstractJsonLinkExtractor { - - static final MediaType HAL_MEDIA_TYPE = new MediaType("application", "hal+json"); - - static final MediaType VND_HAL_MEDIA_TYPE = new MediaType("application", "vnd.hal+json"); - - static final MediaType HAL_FORMS_MEDIA_TYPE = new MediaType("application", "prs.hal-forms+json"); - - @Override - public Map> extractLinks(Map json) { - Map> extractedLinks = new LinkedHashMap<>(); - Object possibleLinks = json.get("_links"); - if (possibleLinks instanceof Map) { - @SuppressWarnings("unchecked") - Map links = (Map) possibleLinks; - for (Entry entry : links.entrySet()) { - String rel = entry.getKey(); - extractedLinks.put(rel, convertToLinks(entry.getValue(), rel)); - } - } - return extractedLinks; - } - - private static List convertToLinks(Object object, String rel) { - List links = new ArrayList<>(); - if (object instanceof Collection) { - @SuppressWarnings("unchecked") - Collection possibleLinkObjects = (Collection) object; - for (Object possibleLinkObject : possibleLinkObjects) { - maybeAddLink(maybeCreateLink(rel, possibleLinkObject), links); - } - } - else { - maybeAddLink(maybeCreateLink(rel, object), links); - } - return links; - } - - private static Link maybeCreateLink(String rel, Object possibleLinkObject) { - if (possibleLinkObject instanceof Map) { - Map possibleLinkMap = (Map) possibleLinkObject; - Object hrefObject = possibleLinkMap.get("href"); - if (hrefObject instanceof String) { - Object titleObject = possibleLinkMap.get("title"); - return new Link(rel, (String) hrefObject, - (titleObject instanceof String) ? (String) titleObject : null); - } - } - return null; - } - - private static void maybeAddLink(Link possibleLink, List links) { - if (possibleLink != null) { - links.add(possibleLink); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java deleted file mode 100644 index 74bacc616..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * Static factory methods for documenting a RESTful API that utilizes Hypermedia. - * - * @author Andy Wilkinson - * @author Marcel Overdijk - */ -public abstract class HypermediaDocumentation { - - private HypermediaDocumentation() { - - } - - /** - * Creates a {@code LinkDescriptor} that describes a link with the given {@code rel}. - * @param rel the rel of the link - * @return a {@code LinkDescriptor} ready for further configuration - */ - public static LinkDescriptor linkWithRel(String rel) { - return new LinkDescriptor(rel); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response automatically based on its - * content type and will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(LinkDescriptor... descriptors) { - return links(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response automatically based on its - * content type and will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(List descriptors) { - return new LinksSnippet(new ContentTypeLinkExtractor(), descriptors); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response automatically based on its - * content type and will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(LinkDescriptor... descriptors) { - return relaxedLinks(Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response automatically based on its - * content type and will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(List descriptors) { - return new LinksSnippet(new ContentTypeLinkExtractor(), descriptors, true); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API call's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response automatically based on its content type - * and will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(Map attributes, LinkDescriptor... descriptors) { - return links(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API call's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response automatically based on its content type - * and will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(Map attributes, List descriptors) { - return new LinksSnippet(new ContentTypeLinkExtractor(), descriptors, attributes); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API call's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response automatically based on its content type - * and will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(Map attributes, LinkDescriptor... descriptors) { - return relaxedLinks(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API call's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response automatically based on its content type - * and will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(Map attributes, List descriptors) { - return new LinksSnippet(new ContentTypeLinkExtractor(), descriptors, attributes, true); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response using the given - * {@code linkExtractor} and will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(LinkExtractor linkExtractor, LinkDescriptor... descriptors) { - return links(linkExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response using the given - * {@code linkExtractor} and will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(LinkExtractor linkExtractor, List descriptors) { - return new LinksSnippet(linkExtractor, descriptors); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response using the given - * {@code linkExtractor} and will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(LinkExtractor linkExtractor, LinkDescriptor... descriptors) { - return relaxedLinks(linkExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. Links will be extracted from the response using the given - * {@code linkExtractor} and will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(LinkExtractor linkExtractor, List descriptors) { - return new LinksSnippet(linkExtractor, descriptors, true); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response using the given {@code linkExtractor} and - * will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(LinkExtractor linkExtractor, Map attributes, - LinkDescriptor... descriptors) { - return links(linkExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response using the given {@code linkExtractor} and - * will be documented using the given {@code descriptors}. - *

- * If a link is present in the response, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a link - * is documented, is not marked as optional, and is not present in the response, a - * failure will also occur. - *

- * If you do not want to document a link, a link descriptor can be marked as - * {@link LinkDescriptor#ignored}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet links(LinkExtractor linkExtractor, Map attributes, - List descriptors) { - return new LinksSnippet(linkExtractor, descriptors, attributes); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response using the given {@code linkExtractor} and - * will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(LinkExtractor linkExtractor, Map attributes, - LinkDescriptor... descriptors) { - return relaxedLinks(linkExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a new {@code Snippet} that will document the links in the API operation's - * response. The given {@code attributes} will be available during snippet generation. - * Links will be extracted from the response using the given {@code linkExtractor} and - * will be documented using the given {@code descriptors}. - *

- * If a link is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented links will be ignored. - *

- * If a descriptor does not have a {@link LinkDescriptor#description(Object) - * description}, the {@link Link#getTitle() title} of the link will be used. If the - * link does not have a title a failure will occur. - * @param attributes the attributes - * @param linkExtractor used to extract the links from the response - * @param descriptors the descriptions of the response's links - * @return the snippet that will document the links - */ - public static LinksSnippet relaxedLinks(LinkExtractor linkExtractor, Map attributes, - List descriptors) { - return new LinksSnippet(linkExtractor, descriptors, attributes, true); - } - - /** - * Returns a {@code LinkExtractor} capable of extracting links in Hypermedia - * Application Language (HAL) format where the links are found in a map named - * {@code _links}. For example: - * - *

-	 * {
-	 *     "_links": {
-	 *         "self": {
-	 *             "href": "https://example.com/foo"
-	 *         }
-	 *     }
-	 * }
-	 * 
- * @return the extractor for HAL-style links - */ - public static LinkExtractor halLinks() { - return new HalLinkExtractor(); - } - - /** - * Returns a {@code LinkExtractor} capable of extracting links in Atom format where - * the links are found in an array named {@code links}. For example: - * - *
-	 * {
-	 *     "links": [
-	 *         {
-	 *             "rel": "self",
-	 *             "href": "https://example.com/foo"
-	 *         }
-	 *     ]
-	 * }
-	 * 
- * @return the extractor for Atom-style links - */ - public static LinkExtractor atomLinks() { - return new AtomLinkExtractor(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/Link.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/Link.java deleted file mode 100644 index ace5f5eff..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/Link.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import org.springframework.core.style.ToStringCreator; - -/** - * Representation of a link used in a Hypermedia-based API. - * - * @author Andy Wilkinson - */ -public class Link { - - private final String rel; - - private final String href; - - private final String title; - - /** - * Creates a new {@code Link} with the given {@code rel} and {@code href}. - * @param rel the link's rel - * @param href the link's href - */ - public Link(String rel, String href) { - this(rel, href, null); - } - - /** - * Creates a new {@code Link} with the given {@code rel}, {@code href}, and - * {@code title}. - * @param rel the link's rel - * @param href the link's href - * @param title the link's title - */ - public Link(String rel, String href, String title) { - this.rel = rel; - this.href = href; - this.title = title; - } - - /** - * Returns the link's {@code rel}. - * @return the link's {@code rel} - */ - public String getRel() { - return this.rel; - } - - /** - * Returns the link's {@code href}. - * @return the link's {@code href} - */ - public String getHref() { - return this.href; - } - - /** - * Returns the link's {@code title}, or {@code null} if it does not have a title. - * @return the link's {@code title} or {@code null} - */ - public String getTitle() { - return this.title; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Link other = (Link) obj; - if (!this.href.equals(other.href)) { - return false; - } - if (!this.rel.equals(other.rel)) { - return false; - } - if (this.title == null) { - if (other.title != null) { - return false; - } - } - else if (!this.title.equals(other.title)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + this.href.hashCode(); - result = prime * result + this.rel.hashCode(); - result = prime * result + ((this.title == null) ? 0 : this.title.hashCode()); - return result; - } - - @Override - public String toString() { - return new ToStringCreator(this).append("rel", this.rel) - .append("href", this.href) - .append("title", this.title) - .toString(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinkDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinkDescriptor.java deleted file mode 100644 index 9e69717e3..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinkDescriptor.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import org.springframework.restdocs.snippet.IgnorableDescriptor; - -/** - * A description of a link found in a hypermedia API. - * - * @author Andy Wilkinson - * @see HypermediaDocumentation#linkWithRel(String) - */ -public class LinkDescriptor extends IgnorableDescriptor { - - private final String rel; - - private boolean optional; - - /** - * Creates a new {@code LinkDescriptor} describing a link with the given {@code rel}. - * @param rel the rel of the link - */ - protected LinkDescriptor(String rel) { - this.rel = rel; - } - - /** - * Marks the link as optional. - * @return {@code this} - */ - public final LinkDescriptor optional() { - this.optional = true; - return this; - } - - /** - * Returns the rel of the link described by this descriptor. - * @return the rel - */ - public final String getRel() { - return this.rel; - } - - /** - * Returns {@code true} if the described link is optional, otherwise {@code false}. - * @return {@code true} if the described link is optional, otherwise {@code false} - */ - public final boolean isOptional() { - return this.optional; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractor.java deleted file mode 100644 index ade697e70..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractor.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.operation.OperationResponse; - -/** - * A {@code LinkExtractor} is used to extract {@link Link links} from a JSON response. The - * expected format of the links in the response is determined by the implementation. - * - * @author Andy Wilkinson - * - */ -public interface LinkExtractor { - - /** - * Extract the links from the given {@code response}, returning a {@code Map} of links - * where the keys are the link rels. - * @param response the response from which the links are to be extracted - * @return the extracted links, keyed by rel - * @throws IOException if link extraction fails - */ - Map> extractLinks(OperationResponse response) throws IOException; - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java deleted file mode 100644 index e5ee2ce8c..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/LinksSnippet.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.snippet.ModelCreationException; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; - -/** - * A {@link Snippet} that documents a RESTful resource's links. - * - * @author Andy Wilkinson - * @see HypermediaDocumentation#links(LinkDescriptor...) - * @see HypermediaDocumentation#links(LinkExtractor, LinkDescriptor...) - * @see HypermediaDocumentation#links(Map, LinkDescriptor...) - * @see HypermediaDocumentation#links(LinkExtractor, Map, LinkDescriptor...) - */ -public class LinksSnippet extends TemplatedSnippet { - - private final Map descriptorsByRel = new LinkedHashMap<>(); - - private final LinkExtractor linkExtractor; - - private final boolean ignoreUndocumentedLinks; - - /** - * Creates a new {@code LinksSnippet} that will extract links using the given - * {@code linkExtractor} and document them using the given {@code descriptors}. - * Undocumented links will trigger a failure. - * @param linkExtractor the link extractor - * @param descriptors the link descriptors - */ - protected LinksSnippet(LinkExtractor linkExtractor, List descriptors) { - this(linkExtractor, descriptors, null, false); - } - - /** - * Creates a new {@code LinksSnippet} that will extract links using the given - * {@code linkExtractor} and document them using the given {@code descriptors}. If - * {@code ignoreUndocumentedLinks} is {@code true}, undocumented links will be ignored - * and will not trigger a failure. - * @param linkExtractor the link extractor - * @param descriptors the link descriptors - * @param ignoreUndocumentedLinks whether undocumented links should be ignored - */ - protected LinksSnippet(LinkExtractor linkExtractor, List descriptors, - boolean ignoreUndocumentedLinks) { - this(linkExtractor, descriptors, null, ignoreUndocumentedLinks); - } - - /** - * Creates a new {@code LinksSnippet} that will extract links using the given - * {@code linkExtractor} and document them using the given {@code descriptors}. The - * given {@code attributes} will be included in the model during template rendering. - * Undocumented links will trigger a failure. - * @param linkExtractor the link extractor - * @param descriptors the link descriptors - * @param attributes the additional attributes - */ - protected LinksSnippet(LinkExtractor linkExtractor, List descriptors, - Map attributes) { - this(linkExtractor, descriptors, attributes, false); - } - - /** - * Creates a new {@code LinksSnippet} that will extract links using the given - * {@code linkExtractor} and document them using the given {@code descriptors}. The - * given {@code attributes} will be included in the model during template rendering. - * If {@code ignoreUndocumentedLinks} is {@code true}, undocumented links will be - * ignored and will not trigger a failure. - * @param linkExtractor the link extractor - * @param descriptors the link descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedLinks whether undocumented links should be ignored - */ - protected LinksSnippet(LinkExtractor linkExtractor, List descriptors, - Map attributes, boolean ignoreUndocumentedLinks) { - super("links", attributes); - this.linkExtractor = linkExtractor; - for (LinkDescriptor descriptor : descriptors) { - Assert.notNull(descriptor.getRel(), "Link descriptors must have a rel"); - this.descriptorsByRel.put(descriptor.getRel(), descriptor); - } - this.ignoreUndocumentedLinks = ignoreUndocumentedLinks; - } - - @Override - protected Map createModel(Operation operation) { - OperationResponse response = operation.getResponse(); - Map> links; - try { - links = this.linkExtractor.extractLinks(response); - validate(links); - } - catch (IOException ex) { - throw new ModelCreationException(ex); - } - Map model = new HashMap<>(); - model.put("links", createLinksModel(links)); - return model; - } - - private void validate(Map> links) { - Set actualRels = links.keySet(); - - Set undocumentedRels; - if (this.ignoreUndocumentedLinks) { - undocumentedRels = Collections.emptySet(); - } - else { - undocumentedRels = new HashSet<>(actualRels); - undocumentedRels.removeAll(this.descriptorsByRel.keySet()); - } - - Set requiredRels = new HashSet<>(); - for (Entry relAndDescriptor : this.descriptorsByRel.entrySet()) { - if (!relAndDescriptor.getValue().isOptional()) { - requiredRels.add(relAndDescriptor.getKey()); - } - } - - Set missingRels = new HashSet<>(requiredRels); - missingRels.removeAll(actualRels); - - if (!undocumentedRels.isEmpty() || !missingRels.isEmpty()) { - String message = ""; - if (!undocumentedRels.isEmpty()) { - message += "Links with the following relations were not documented: " + undocumentedRels; - } - if (!missingRels.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Links with the following relations were not found in the " + "response: " + missingRels; - } - throw new SnippetException(message); - } - } - - private List> createLinksModel(Map> links) { - List> model = new ArrayList<>(); - for (Entry entry : this.descriptorsByRel.entrySet()) { - LinkDescriptor descriptor = entry.getValue(); - if (!descriptor.isIgnored()) { - if (descriptor.getDescription() == null) { - descriptor = createDescriptor(getDescriptionFromLinkTitle(links, descriptor.getRel()), descriptor); - } - model.add(createModelForDescriptor(descriptor)); - } - } - return model; - } - - private String getDescriptionFromLinkTitle(Map> links, String rel) { - List linksForRel = links.get(rel); - if (linksForRel != null) { - for (Link link : linksForRel) { - if (link.getTitle() != null) { - return link.getTitle(); - } - } - } - throw new SnippetException("No description was provided for the link with rel '" + rel - + "' and no title was available from the link in the payload"); - } - - private LinkDescriptor createDescriptor(String description, LinkDescriptor source) { - LinkDescriptor newDescriptor = new LinkDescriptor(source.getRel()).description(description); - if (source.isOptional()) { - newDescriptor.optional(); - } - if (source.isIgnored()) { - newDescriptor.ignored(); - } - return newDescriptor; - } - - /** - * Returns a {@code Map} of {@link LinkDescriptor LinkDescriptors} keyed by their - * {@link LinkDescriptor#getRel() rels}. - * @return the link descriptors - */ - protected final Map getDescriptorsByRel() { - return this.descriptorsByRel; - } - - /** - * Returns a model for the given {@code descriptor}. - * @param descriptor the descriptor - * @return the model - */ - protected Map createModelForDescriptor(LinkDescriptor descriptor) { - Map model = new HashMap<>(); - model.put("rel", descriptor.getRel()); - model.put("description", descriptor.getDescription()); - model.put("optional", descriptor.isOptional()); - model.putAll(descriptor.getAttributes()); - return model; - } - - /** - * Returns a new {@code LinksSnippet} configured with this snippet's link extractor - * and attributes, and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final LinksSnippet and(LinkDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code LinksSnippet} configured with this snippet's link extractor - * and attributes, and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final LinksSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(this.descriptorsByRel.values()); - combinedDescriptors.addAll(additionalDescriptors); - return new LinksSnippet(this.linkExtractor, combinedDescriptors, getAttributes()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/package-info.java deleted file mode 100644 index d42d90770..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/hypermedia/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting a RESTful API that uses hypermedia. - */ -package org.springframework.restdocs.hypermedia; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java deleted file mode 100644 index 038831d20..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; - -/** - * Abstract base class for operation requests, request parts, and responses. - * - * @author Andy Wilkinson - */ -abstract class AbstractOperationMessage implements OperationMessage { - - private final byte[] content; - - private final HttpHeaders headers; - - AbstractOperationMessage(byte[] content, HttpHeaders headers) { - this.content = (content != null) ? content : new byte[0]; - this.headers = headers; - } - - @Override - public byte[] getContent() { - return Arrays.copyOf(this.content, this.content.length); - } - - @Override - public HttpHeaders getHeaders() { - return HttpHeaders.readOnlyHttpHeaders(this.headers); - } - - @Override - public String getContentAsString() { - if (this.content.length > 0) { - Charset charset = extractCharsetFromContentTypeHeader(); - if (charset == null) { - charset = StandardCharsets.UTF_8; - } - return new String(this.content, charset); - } - return ""; - } - - private Charset extractCharsetFromContentTypeHeader() { - if (this.headers == null) { - return null; - } - MediaType contentType = this.headers.getContentType(); - if (contentType == null) { - return null; - } - return contentType.getCharset(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java deleted file mode 100644 index 03ffd5e3d..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ConversionException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -/** - * An exception that can be thrown by {@link RequestConverter} and - * {@link ResponseConverter} implementations to indicate that a failure has occurred - * during conversion. - * - * @author Andy Wilkinson - * @since 1.1.0 - * @see RequestConverter#convert(Object) - * @see ResponseConverter#convert(Object) - */ -public class ConversionException extends RuntimeException { - - /** - * Creates a new {@code ConversionException} with the given {@code cause}. - * @param cause the cause - */ - public ConversionException(Throwable cause) { - super(cause); - } - - /** - * Creates a new {@code ConversionException} with the given {@code message} and - * {@code cause}. - * @param message the message - * @param cause the cause - */ - public ConversionException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/FormParameters.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/FormParameters.java deleted file mode 100644 index 5cabb84e3..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/FormParameters.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.LinkedList; -import java.util.List; -import java.util.Scanner; - -import org.springframework.util.LinkedMultiValueMap; - -/** - * A request's form parameters, derived from its form URL encoded body content. - * - * @author Andy Wilkinson - * @since 3.0.0 - */ -public final class FormParameters extends LinkedMultiValueMap { - - private FormParameters() { - - } - - /** - * Extracts the form parameters from the body of the given {@code request}. If the - * request has no body content, an empty {@code FormParameters} is returned, rather - * than {@code null}. - * @param request the request - * @return the form parameters extracted from the body content - */ - public static FormParameters from(OperationRequest request) { - return of(request.getContentAsString()); - } - - private static FormParameters of(String bodyContent) { - if (bodyContent == null || bodyContent.length() == 0) { - return new FormParameters(); - } - return parse(bodyContent); - } - - private static FormParameters parse(String bodyContent) { - FormParameters parameters = new FormParameters(); - try (Scanner scanner = new Scanner(bodyContent)) { - scanner.useDelimiter("&"); - while (scanner.hasNext()) { - processParameter(scanner.next(), parameters); - } - } - return parameters; - } - - private static void processParameter(String parameter, FormParameters parameters) { - String[] components = parameter.split("="); - if (components.length > 0 && components.length < 3) { - if (components.length == 2) { - String name = components[0]; - String value = components[1]; - parameters.add(decode(name), decode(value)); - } - else { - List values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<>()); - values.add(""); - } - } - else { - throw new IllegalArgumentException("The parameter '" + parameter + "' is malformed"); - } - } - - private static String decode(String encoded) { - try { - return URLDecoder.decode(encoded, "UTF-8"); - } - catch (UnsupportedEncodingException ex) { - throw new IllegalStateException("Unable to URL decode " + encoded + " using UTF-8", ex); - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java deleted file mode 100644 index 240b4f6d8..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import org.springframework.http.HttpHeaders; - -/** - * Helper for working with {@link HttpHeaders}. - * - * @author Andy Wilkinson - */ -class HttpHeadersHelper { - - private final HttpHeaders httpHeaders; - - HttpHeadersHelper(HttpHeaders httpHeaders) { - HttpHeaders headers = new HttpHeaders(); - if (httpHeaders != null) { - headers.putAll(httpHeaders); - } - this.httpHeaders = headers; - } - - HttpHeadersHelper addIfAbsent(String name, String value) { - if (this.httpHeaders.get(name) == null) { - this.httpHeaders.add(name, value); - } - return this; - } - - HttpHeadersHelper updateContentLengthHeaderIfPresent(byte[] content) { - if (this.httpHeaders.getContentLength() != -1) { - setContentLengthHeader(content); - } - return this; - } - - HttpHeadersHelper setContentLengthHeader(byte[] content) { - if (content == null || content.length == 0) { - this.httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); - } - else { - this.httpHeaders.setContentLength(content.length); - } - return this; - } - - HttpHeaders getHeaders() { - return HttpHeaders.readOnlyHttpHeaders(this.httpHeaders); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/Operation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/Operation.java deleted file mode 100644 index a4d956ebe..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/Operation.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.util.Map; - -/** - * Describes an operation performed on a RESTful service. - * - * @author Andy Wilkinson - */ -public interface Operation { - - /** - * Returns a {@code Map} of attributes associated with the operation. - * @return the attributes - */ - Map getAttributes(); - - /** - * Returns the name of the operation. - * @return the name - */ - String getName(); - - /** - * Returns the request that was sent. - * @return the request - */ - OperationRequest getRequest(); - - /** - * Returns the response that was received. - * @return the response - */ - OperationResponse getResponse(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationMessage.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationMessage.java deleted file mode 100644 index 7570c5ed1..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationMessage.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import org.springframework.http.HttpHeaders; - -/** - * Base contract for operation requests, request parts, and responses. - * - * @author Andy Wilkinson - */ -interface OperationMessage { - - byte[] getContent(); - - String getContentAsString(); - - HttpHeaders getHeaders(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequest.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequest.java deleted file mode 100644 index 7e36c2f37..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.net.URI; -import java.util.Collection; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; - -/** - * The request that was sent as part of performing an operation on a RESTful service. - * - * @author Andy Wilkinson - * @see Operation#getRequest() - */ -public interface OperationRequest { - - /** - * Returns the content of the request. If the request has no content an empty array is - * returned. - * @return the contents, never {@code null} - */ - byte[] getContent(); - - /** - * Returns the content of the request as a {@link String}. If the request has no - * content an empty string is returned. If the request has a {@code Content-Type} - * header that specifies a charset then that charset will be used when converting the - * contents to a {@code String}. - * @return the contents as string, never {@code null} - */ - String getContentAsString(); - - /** - * Returns the headers that were included in the request. - * @return the headers - */ - HttpHeaders getHeaders(); - - /** - * Returns the HTTP method of the request. - * @return the HTTP method - */ - HttpMethod getMethod(); - - /** - * Returns the request's parts, provided that it is a multipart request. If not, then - * an empty {@link Collection} is returned. - * @return the parts - */ - Collection getParts(); - - /** - * Returns the request's URI. - * @return the URI - */ - URI getUri(); - - /** - * Returns the {@link RequestCookie cookies} sent with the request. If no cookies were - * sent an empty collection is returned. - * @return the cookies, never {@code null} - * @since 1.2.0 - */ - Collection getCookies(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java deleted file mode 100644 index 39d0f5f73..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.net.URI; -import java.util.Collection; -import java.util.Collections; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; - -/** - * A factory for creating {@link OperationRequest OperationRequests}. - * - * @author Andy Wilkinson - */ -public class OperationRequestFactory { - - /** - * Creates a new {@link OperationRequest}. The given {@code headers} will be augmented - * to ensure that they always include a {@code Content-Length} header if the request - * has any content and a {@code Host} header. - * @param uri the request's uri - * @param method the request method - * @param content the content of the request - * @param headers the request's headers - * @param parts the request's parts - * @param cookies the request's cookies - * @return the {@code OperationRequest} - * @since 3.0.0 - */ - public OperationRequest create(URI uri, HttpMethod method, byte[] content, HttpHeaders headers, - Collection parts, Collection cookies) { - return new StandardOperationRequest(uri, method, content, augmentHeaders(headers, uri, content), - (parts != null) ? parts : Collections.emptyList(), cookies); - } - - /** - * Creates a new {@link OperationRequest}. The given {@code headers} will be augmented - * to ensure that they always include a {@code Content-Length} header if the request - * has any content and a {@code Host} header. - * @param uri the request's uri - * @param method the request method - * @param content the content of the request - * @param headers the request's headers - * @param parts the request's parts - * @return the {@code OperationRequest} - * @since 3.0.0 - */ - public OperationRequest create(URI uri, HttpMethod method, byte[] content, HttpHeaders headers, - Collection parts) { - return create(uri, method, content, headers, parts, Collections.emptyList()); - } - - /** - * Creates a new {@code OperationRequest} based on the given {@code original} but with - * the given {@code newContent}. If the original request had a {@code Content-Length} - * header it will be modified to match the length of the new content. - * @param original the original request - * @param newContent the new content - * @return the new request with the new content - */ - public OperationRequest createFrom(OperationRequest original, byte[] newContent) { - return new StandardOperationRequest(original.getUri(), original.getMethod(), newContent, - getUpdatedHeaders(original.getHeaders(), newContent), original.getParts(), original.getCookies()); - } - - /** - * Creates a new {@code OperationRequest} based on the given {@code original} but with - * the given {@code newHeaders}. - * @param original the original request - * @param newHeaders the new headers - * @return the new request with the new headers - */ - public OperationRequest createFrom(OperationRequest original, HttpHeaders newHeaders) { - return new StandardOperationRequest(original.getUri(), original.getMethod(), original.getContent(), newHeaders, - original.getParts(), original.getCookies()); - } - - private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, URI uri, byte[] content) { - return new HttpHeadersHelper(originalHeaders).addIfAbsent(HttpHeaders.HOST, createHostHeader(uri)) - .setContentLengthHeader(content) - .getHeaders(); - } - - private String createHostHeader(URI uri) { - if (uri.getPort() == -1) { - return uri.getHost(); - } - return uri.getHost() + ":" + uri.getPort(); - } - - private HttpHeaders getUpdatedHeaders(HttpHeaders originalHeaders, byte[] updatedContent) { - return new HttpHeadersHelper(originalHeaders).updateContentLengthHeaderIfPresent(updatedContent).getHeaders(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPart.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPart.java deleted file mode 100644 index 77dd7f2dc..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPart.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import org.springframework.http.HttpHeaders; - -/** - * A part of a multipart request. - * - * @author Andy Wilkinson - * @see OperationRequest#getParts() - */ -public interface OperationRequestPart { - - /** - * Returns the name of the part. - * @return the name - */ - String getName(); - - /** - * Returns the name of the file that is being uploaded in this part. - * @return the name of the file - */ - String getSubmittedFileName(); - - /** - * Returns the contents of the part. - * @return the contents - */ - byte[] getContent(); - - /** - * Returns the content of the part as a {@link String}. If the part has no content an - * empty string is returned. If the part has a {@code Content-Type} header that - * specifies a charset then that charset will be used when converting the contents to - * a {@code String}. - * @return the contents as string, never {@code null} - */ - String getContentAsString(); - - /** - * Returns the part's headers. - * @return the headers - */ - HttpHeaders getHeaders(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java deleted file mode 100644 index d9fe354c6..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import org.springframework.http.HttpHeaders; - -/** - * A factory for creating {@link OperationRequestPart OperationRequestParts}. - * - * @author Andy Wilkinson - */ -public class OperationRequestPartFactory { - - /** - * Creates a new {@link OperationRequestPart}. The given {@code headers} will be - * augmented to ensure that they always include a {@code Content-Length} header if the - * part has any content. - * @param name the name of the part - * @param submittedFileName the name of the file being submitted by the part - * @param content the content of the part - * @param headers the headers of the part - * @return the {@code OperationRequestPart} - */ - public OperationRequestPart create(String name, String submittedFileName, byte[] content, HttpHeaders headers) { - return new StandardOperationRequestPart(name, submittedFileName, content, augmentHeaders(headers, content)); - } - - private HttpHeaders augmentHeaders(HttpHeaders input, byte[] content) { - return new HttpHeadersHelper(input).setContentLengthHeader(content).getHeaders(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java deleted file mode 100644 index 344b4e9a9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponse.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.util.Collection; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatusCode; - -/** - * The response that was received as part of performing an operation on a RESTful service. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - * @see Operation - * @see Operation#getRequest() - */ -public interface OperationResponse { - - /** - * Returns the status of the response. - * @return the status, never {@code null} - */ - HttpStatusCode getStatus(); - - /** - * Returns the headers in the response. - * @return the headers - */ - HttpHeaders getHeaders(); - - /** - * Returns the content of the response. If the response has no content an empty array - * is returned. - * @return the contents, never {@code null} - */ - byte[] getContent(); - - /** - * Returns the content of the response as a {@link String}. If the response has no - * content an empty string is returned. If the response has a {@code Content-Type} - * header that specifies a charset then that charset will be used when converting the - * contents to a {@code String}. - * @return the contents as string, never {@code null} - */ - String getContentAsString(); - - /** - * Returns the {@link ResponseCookie cookies} returned with the response. If no - * cookies were returned an empty collection is returned. - * @return the cookies, never {@code null} - * @since 3.0 - */ - Collection getCookies(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java deleted file mode 100644 index b9c749ab1..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.util.Collection; -import java.util.Collections; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatusCode; - -/** - * A factory for creating {@link OperationResponse OperationResponses}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -public class OperationResponseFactory { - - /** - * Creates a new {@link OperationResponse} without cookies. If the response has any - * content, the given {@code headers} will be augmented to ensure that they include a - * {@code Content-Length} header. - * @param status the status of the response - * @param headers the request's headers - * @param content the content of the request - * @return the {@code OperationResponse} - * @since 3.0.0 - */ - public OperationResponse create(HttpStatusCode status, HttpHeaders headers, byte[] content) { - return new StandardOperationResponse(status, augmentHeaders(headers, content), content, - Collections.emptyList()); - } - - /** - * Creates a new {@link OperationResponse}. If the response has any content, the given - * {@code headers} will be augmented to ensure that they include a - * {@code Content-Length} header. - * @param status the status of the response - * @param headers the request's headers - * @param content the content of the request - * @param cookies the cookies - * @return the {@code OperationResponse} - * @since 3.0.0 - */ - public OperationResponse create(HttpStatusCode status, HttpHeaders headers, byte[] content, - Collection cookies) { - return new StandardOperationResponse(status, augmentHeaders(headers, content), content, cookies); - } - - /** - * Creates a new {@code OperationResponse} based on the given {@code original} but - * with the given {@code newContent}. If the original response had a - * {@code Content-Length} header it will be modified to match the length of the new - * content. - * @param original the original response - * @param newContent the new content - * @return the new response with the new content - */ - public OperationResponse createFrom(OperationResponse original, byte[] newContent) { - return new StandardOperationResponse(original.getStatus(), getUpdatedHeaders(original.getHeaders(), newContent), - newContent, original.getCookies()); - } - - /** - * Creates a new {@code OperationResponse} based on the given {@code original} but - * with the given {@code newHeaders}. - * @param original the original response - * @param newHeaders the new headers - * @return the new response with the new headers - */ - public OperationResponse createFrom(OperationResponse original, HttpHeaders newHeaders) { - return new StandardOperationResponse(original.getStatus(), newHeaders, original.getContent(), - original.getCookies()); - } - - private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, byte[] content) { - return new HttpHeadersHelper(originalHeaders).setContentLengthHeader(content).getHeaders(); - } - - private HttpHeaders getUpdatedHeaders(HttpHeaders originalHeaders, byte[] updatedContent) { - return new HttpHeadersHelper(originalHeaders).updateContentLengthHeaderIfPresent(updatedContent).getHeaders(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/QueryParameters.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/QueryParameters.java deleted file mode 100644 index 17f7fc1c6..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/QueryParameters.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.LinkedList; -import java.util.List; -import java.util.Scanner; - -import org.springframework.util.LinkedMultiValueMap; - -/** - * A request's query parameters, derived from its URI's query string. - * - * @author Andy Wilkinson - * @since 3.0.0 - */ -public final class QueryParameters extends LinkedMultiValueMap { - - private QueryParameters() { - - } - - /** - * Extracts the query parameters from the query string of the given {@code request}. - * If the request has no query string, an empty {@code QueryParameters} is returned, - * rather than {@code null}. - * @param request the request - * @return the query parameters extracted from the request's query string - */ - public static QueryParameters from(OperationRequest request) { - return from(request.getUri().getRawQuery()); - } - - private static QueryParameters from(String queryString) { - if (queryString == null || queryString.length() == 0) { - return new QueryParameters(); - } - return parse(queryString); - } - - private static QueryParameters parse(String query) { - QueryParameters parameters = new QueryParameters(); - try (Scanner scanner = new Scanner(query)) { - scanner.useDelimiter("&"); - while (scanner.hasNext()) { - processParameter(scanner.next(), parameters); - } - } - return parameters; - } - - private static void processParameter(String parameter, QueryParameters parameters) { - String[] components = parameter.split("="); - if (components.length > 0 && components.length < 3) { - if (components.length == 2) { - String name = components[0]; - String value = components[1]; - parameters.add(decode(name), decode(value)); - } - else { - List values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<>()); - values.add(""); - } - } - else { - throw new IllegalArgumentException("The parameter '" + parameter + "' is malformed"); - } - } - - private static String decode(String encoded) { - return URLDecoder.decode(encoded, StandardCharsets.UTF_8); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java deleted file mode 100644 index 896893107..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestConverter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -/** - * A {@code RequestConverter} is used to convert an implementation-specific request into - * an {@link OperationRequest}. - * - * @param the implementation-specific request type - * @author Andy Wilkinson - * @since 1.1.0 - */ -public interface RequestConverter { - - /** - * Converts the given {@code request} into an {@code OperationRequest}. - * @param request the request - * @return the operation request - * @throws ConversionException if the conversion fails - */ - OperationRequest convert(R request); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestCookie.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestCookie.java deleted file mode 100644 index 4211ef12c..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/RequestCookie.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -/** - * A representation of a Cookie received in a request. - * - * @author Andy Wilkinson - * @since 1.2.0 - */ -public final class RequestCookie { - - private final String name; - - private final String value; - - /** - * Creates a new {@code RequestCookie} with the given {@code name} and {@code value}. - * @param name the name of the cookie - * @param value the value of the cookie - */ - public RequestCookie(String name, String value) { - this.name = name; - this.value = value; - } - - /** - * Returns the name of the cookie. - * @return the name - */ - public String getName() { - return this.name; - } - - /** - * Returns the value of the cookie. - * @return the value - */ - public String getValue() { - return this.value; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java deleted file mode 100644 index 6955b9c37..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseConverter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -/** - * A {@code ResponseConverter} is used to convert an implementation-specific response into - * an {@link OperationResponse}. - * - * @param the implementation-specific response type - * @author Andy Wilkinson - * @since 1.1.0 - */ -public interface ResponseConverter { - - /** - * Converts the given {@code response} into an {@code OperationResponse}. - * @param response the response - * @return the operation response - * @throws ConversionException if the conversion fails - */ - OperationResponse convert(R response); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseCookie.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseCookie.java deleted file mode 100644 index 9cf39302e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/ResponseCookie.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -/** - * A representation of a Cookie returned in a response. - * - * @author Clyde Stubbs - * @since 3.0 - */ -public final class ResponseCookie { - - private final String name; - - private final String value; - - /** - * Creates a new {@code ResponseCookie} with the given {@code name} and {@code value}. - * @param name the name of the cookie - * @param value the value of the cookie - */ - public ResponseCookie(String name, String value) { - this.name = name; - this.value = value; - } - - /** - * Returns the name of the cookie. - * @return the name - */ - public String getName() { - return this.name; - } - - /** - * Returns the value of the cookie. - * @return the value - */ - public String getValue() { - return this.value; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperation.java deleted file mode 100644 index 14a62de2e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperation.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.util.Map; - -/** - * Standard implementation of {@link Operation}. - * - * @author Andy Wilkinson - */ -public class StandardOperation implements Operation { - - private final String name; - - private final OperationRequest request; - - private final OperationResponse response; - - private final Map attributes; - - /** - * Creates a new {@code StandardOperation}. - * @param name the name of the operation - * @param request the request that was sent - * @param response the response that was received - * @param attributes attributes to associate with the operation - */ - public StandardOperation(String name, OperationRequest request, OperationResponse response, - Map attributes) { - this.name = name; - this.request = request; - this.response = response; - this.attributes = attributes; - } - - @Override - public Map getAttributes() { - return this.attributes; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public OperationRequest getRequest() { - return this.request; - } - - @Override - public OperationResponse getResponse() { - return this.response; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java deleted file mode 100644 index bdf65934a..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.net.URI; -import java.util.Collection; -import java.util.Collections; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; - -/** - * Standard implementation of {@link OperationRequest}. - * - * @author Andy Wilkinson - */ -class StandardOperationRequest extends AbstractOperationMessage implements OperationRequest { - - private HttpMethod method; - - private Collection parts; - - private URI uri; - - private Collection cookies; - - /** - * Creates a new request with the given {@code uri} and {@code method}. The request - * will have the given {@code headers}, {@code parameters}, {@code parts}, and - * {@code cookies}. - * @param uri the uri - * @param method the method - * @param content the content - * @param headers the headers - * @param parts the parts - * @param cookies the cookies - */ - StandardOperationRequest(URI uri, HttpMethod method, byte[] content, HttpHeaders headers, - Collection parts, Collection cookies) { - super(content, headers); - this.uri = uri; - this.method = method; - this.parts = parts; - this.cookies = cookies; - } - - @Override - public HttpMethod getMethod() { - return this.method; - } - - @Override - public Collection getParts() { - return Collections.unmodifiableCollection(this.parts); - } - - @Override - public URI getUri() { - return this.uri; - } - - @Override - public Collection getCookies() { - return this.cookies; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java deleted file mode 100644 index 2a9e9b50e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import org.springframework.http.HttpHeaders; - -/** - * Standard implementation of {@code OperationRequestPart}. - * - * @author Andy Wilkinson - */ -class StandardOperationRequestPart extends AbstractOperationMessage implements OperationRequestPart { - - private final String name; - - private final String submittedFileName; - - /** - * Creates a new {@code StandardOperationRequestPart} with the given {@code name}. - * @param name the name of the part - * @param submittedFileName the name of the file being uploaded by this part - * @param content the contents of the part - * @param headers the headers of the part - */ - StandardOperationRequestPart(String name, String submittedFileName, byte[] content, HttpHeaders headers) { - super(content, headers); - this.name = name; - this.submittedFileName = submittedFileName; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getSubmittedFileName() { - return this.submittedFileName; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java deleted file mode 100644 index 667d03ca8..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation; - -import java.util.Collection; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatusCode; - -/** - * Standard implementation of {@link OperationResponse}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -class StandardOperationResponse extends AbstractOperationMessage implements OperationResponse { - - private final HttpStatusCode status; - - private Collection cookies; - - /** - * Creates a new response with the given {@code status}, {@code headers}, and - * {@code content}. - * @param status the status of the response - * @param headers the headers of the response - * @param content the content of the response - * @param cookies any cookies included in the response - */ - StandardOperationResponse(HttpStatusCode status, HttpHeaders headers, byte[] content, - Collection cookies) { - super(content, headers); - this.status = status; - this.cookies = cookies; - } - - @Override - public HttpStatusCode getStatus() { - return this.status; - } - - @Override - public Collection getCookies() { - return this.cookies; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/package-info.java deleted file mode 100644 index 704ca0ecb..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Operation API that describes a request that was sent and the response that was received - * when calling a RESTful API. - */ -package org.springframework.restdocs.operation; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifier.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifier.java deleted file mode 100644 index e840f960f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifier.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationResponse; - -/** - * A {@code ContentModifier} modifies the content of an {@link OperationRequest} or - * {@link OperationResponse} during the preprocessing that is performed prior to - * documentation generation. - * - * @author Andy Wilkinson - * @see ContentModifyingOperationPreprocessor - */ -public interface ContentModifier { - - /** - * Returns modified content based on the given {@code originalContent}. - * @param originalContent the original content - * @param contentType the type of the original content, may be {@code null} - * @return the modified content - */ - byte[] modifyContent(byte[] originalContent, MediaType contentType); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java deleted file mode 100644 index 82bacd235..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; - -/** - * An {@link OperationPreprocessor} that applies a {@link ContentModifier} to the content - * of the request or response. - * - * @author Andy Wilkinson - */ -public class ContentModifyingOperationPreprocessor implements OperationPreprocessor { - - private final OperationRequestFactory requestFactory = new OperationRequestFactory(); - - private final OperationResponseFactory responseFactory = new OperationResponseFactory(); - - private final ContentModifier contentModifier; - - /** - * Create a new {@code ContentModifyingOperationPreprocessor} that will apply the - * given {@code contentModifier} to the operation's request or response. - * @param contentModifier the contentModifier - */ - public ContentModifyingOperationPreprocessor(ContentModifier contentModifier) { - this.contentModifier = contentModifier; - } - - @Override - public OperationRequest preprocess(OperationRequest request) { - byte[] modifiedContent = this.contentModifier.modifyContent(request.getContent(), - request.getHeaders().getContentType()); - return this.requestFactory.createFrom(request, modifiedContent); - } - - @Override - public OperationResponse preprocess(OperationResponse response) { - byte[] modifiedContent = this.contentModifier.modifyContent(response.getContent(), - response.getHeaders().getContentType()); - return this.responseFactory.createFrom(response, modifiedContent); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessor.java deleted file mode 100644 index f99b4b111..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessor.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.List; - -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.util.Assert; - -/** - * An {@link OperationRequestPreprocessor} that delgates to one or more - * {@link OperationPreprocessor OperationPreprocessors} to preprocess an - * {@link OperationRequest}. - * - * @author Andy Wilkinson - * - */ -class DelegatingOperationRequestPreprocessor implements OperationRequestPreprocessor { - - private final List delegates; - - /** - * Creates a new {@code DelegatingOperationRequestPreprocessor} that will delegate to - * the given {@code delegates} by calling - * {@link OperationPreprocessor#preprocess(OperationRequest)}. - * @param delegates the delegates - */ - DelegatingOperationRequestPreprocessor(List delegates) { - Assert.notNull(delegates, "delegates must be non-null"); - this.delegates = delegates; - } - - @Override - public OperationRequest preprocess(OperationRequest operationRequest) { - OperationRequest preprocessedRequest = operationRequest; - for (OperationPreprocessor delegate : this.delegates) { - preprocessedRequest = delegate.preprocess(preprocessedRequest); - } - return preprocessedRequest; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessor.java deleted file mode 100644 index 48345eaf9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessor.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.List; - -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.util.Assert; - -/** - * An {@link OperationResponsePreprocessor} that delgates to one or more - * {@link OperationPreprocessor OperationPreprocessors} to preprocess an - * {@link OperationResponse}. - * - * @author Andy Wilkinson - */ -class DelegatingOperationResponsePreprocessor implements OperationResponsePreprocessor { - - private final List delegates; - - /** - * Creates a new {@code DelegatingOperationResponsePreprocessor} that will delegate to - * the given {@code delegates} by calling - * {@link OperationPreprocessor#preprocess(OperationResponse)}. - * @param delegates the delegates - */ - DelegatingOperationResponsePreprocessor(List delegates) { - Assert.notNull(delegates, "delegates must be non-null"); - this.delegates = delegates; - } - - @Override - public OperationResponse preprocess(OperationResponse response) { - OperationResponse preprocessedResponse = response; - for (OperationPreprocessor delegate : this.delegates) { - preprocessedResponse = delegate.preprocess(preprocessedResponse); - } - return preprocessedResponse; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ExactMatchHeaderFilter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ExactMatchHeaderFilter.java deleted file mode 100644 index aec331bdb..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ExactMatchHeaderFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * A {@link HeaderFilter} that excludes a header if its name is an exact match. - * - * @author Andy Wilkinson - */ -class ExactMatchHeaderFilter implements HeaderFilter { - - private final Set headersToExclude; - - ExactMatchHeaderFilter(String... headersToExclude) { - this.headersToExclude = new HashSet<>(Arrays.asList(headersToExclude)); - } - - @Override - public boolean excludeHeader(String name) { - return this.headersToExclude.contains(name); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderFilter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderFilter.java deleted file mode 100644 index 744a2334e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -/** - * A strategy for determining whether or not a header should be excluded. - * - * @author Andy Wilkinson - */ -interface HeaderFilter { - - /** - * Called to determine whether a header should be excluded. Return {@code true} to - * exclude a header, otherwise {@code false}. - * @param name the name of the header - * @return {@code true} to exclude the header, otherwise {@code false} - */ - boolean excludeHeader(String name); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java deleted file mode 100644 index 001f7789d..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessor.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.http.HttpHeaders; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.util.Assert; - -/** - * An {@link OperationPreprocessor} that modifies a request or response by adding, - * setting, or removing headers. - * - * @author Jihoon Cha - * @author Andy Wilkinson - * @since 3.0.0 - */ -public class HeadersModifyingOperationPreprocessor implements OperationPreprocessor { - - private final OperationRequestFactory requestFactory = new OperationRequestFactory(); - - private final OperationResponseFactory responseFactory = new OperationResponseFactory(); - - private final List modifications = new ArrayList<>(); - - @Override - public OperationRequest preprocess(OperationRequest request) { - return this.requestFactory.createFrom(request, preprocess(request.getHeaders())); - } - - @Override - public OperationResponse preprocess(OperationResponse response) { - return this.responseFactory.createFrom(response, preprocess(response.getHeaders())); - } - - private HttpHeaders preprocess(HttpHeaders headers) { - HttpHeaders modifiedHeaders = new HttpHeaders(); - modifiedHeaders.addAll(headers); - for (Modification modification : this.modifications) { - modification.applyTo(modifiedHeaders); - } - return modifiedHeaders; - } - - /** - * Adds a header with the given {@code name} and {@code value}. - * @param name the name - * @param value the value - * @return {@code this} - */ - public HeadersModifyingOperationPreprocessor add(String name, String value) { - this.modifications.add(new AddHeaderModification(name, value)); - return this; - } - - /** - * Sets the header with the given {@code name} to have the given {@code values}. - * @param name the name - * @param values the values - * @return {@code this} - */ - public HeadersModifyingOperationPreprocessor set(String name, String... values) { - Assert.notEmpty(values, "At least one value must be provided"); - this.modifications.add(new SetHeaderModification(name, Arrays.asList(values))); - return this; - } - - /** - * Removes the header with the given {@code name}. - * @param name the name of the parameter - * @return {@code this} - */ - public HeadersModifyingOperationPreprocessor remove(String name) { - this.modifications.add(new RemoveHeaderModification(name)); - return this; - } - - /** - * Removes the given {@code value} from the header with the given {@code name}. - * @param name the name - * @param value the value - * @return {@code this} - */ - public HeadersModifyingOperationPreprocessor remove(String name, String value) { - this.modifications.add(new RemoveValueHeaderModification(name, value)); - return this; - } - - /** - * Remove headers that match the given {@code namePattern} regular expression. - * @param namePattern the name pattern - * @return {@code this} - * @see Matcher#matches() - */ - public HeadersModifyingOperationPreprocessor removeMatching(String namePattern) { - this.modifications.add(new RemoveHeadersByNamePatternModification(Pattern.compile(namePattern))); - return this; - } - - private interface Modification { - - void applyTo(HttpHeaders headers); - - } - - private static final class AddHeaderModification implements Modification { - - private final String name; - - private final String value; - - private AddHeaderModification(String name, String value) { - this.name = name; - this.value = value; - } - - @Override - public void applyTo(HttpHeaders headers) { - headers.add(this.name, this.value); - } - - } - - private static final class SetHeaderModification implements Modification { - - private final String name; - - private final List values; - - private SetHeaderModification(String name, List values) { - this.name = name; - this.values = values; - } - - @Override - public void applyTo(HttpHeaders headers) { - headers.put(this.name, this.values); - } - - } - - private static final class RemoveHeaderModification implements Modification { - - private final String name; - - private RemoveHeaderModification(String name) { - this.name = name; - } - - @Override - public void applyTo(HttpHeaders headers) { - headers.remove(this.name); - } - - } - - private static final class RemoveValueHeaderModification implements Modification { - - private final String name; - - private final String value; - - private RemoveValueHeaderModification(String name, String value) { - this.name = name; - this.value = value; - } - - @Override - public void applyTo(HttpHeaders headers) { - List values = headers.get(this.name); - if (values != null) { - values.remove(this.value); - if (values.isEmpty()) { - headers.remove(this.name); - } - } - } - - } - - private static final class RemoveHeadersByNamePatternModification implements Modification { - - private final Pattern namePattern; - - private RemoveHeadersByNamePatternModification(Pattern namePattern) { - this.namePattern = namePattern; - } - - @Override - public void applyTo(HttpHeaders headers) { - headers.headerNames().removeIf((name) -> this.namePattern.matcher(name).matches()); - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifier.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifier.java deleted file mode 100644 index 09b12ff95..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifier.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.nio.charset.StandardCharsets; -import java.util.regex.Pattern; - -import org.springframework.http.MediaType; - -/** - * A content modifier the masks the {@code href} of any hypermedia links. - * - * @author Andy Wilkinson - */ -class LinkMaskingContentModifier implements ContentModifier { - - private static final String DEFAULT_MASK = "..."; - - private static final Pattern LINK_HREF = Pattern.compile("\"href\"\\s*:\\s*\"(.*?)\"", Pattern.DOTALL); - - private final ContentModifier contentModifier; - - LinkMaskingContentModifier() { - this(DEFAULT_MASK); - } - - LinkMaskingContentModifier(String mask) { - this.contentModifier = new PatternReplacingContentModifier(LINK_HREF, mask, StandardCharsets.UTF_8); - } - - @Override - public byte[] modifyContent(byte[] originalContent, MediaType contentType) { - return this.contentModifier.modifyContent(originalContent, contentType); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationPreprocessor.java deleted file mode 100644 index 6b8fd1446..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationPreprocessor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationResponse; - -/** - * An {@code OperationPreprocessor} processes the {@link OperationRequest} and - * {@link OperationResponse} of an {@link Operation} prior to it being documented. - * - * @author Andy Wilkinson - */ -public interface OperationPreprocessor { - - /** - * Processes the given {@code request}. - * @param request the request to process - * @return the processed request - */ - OperationRequest preprocess(OperationRequest request); - - /** - * Processes the given {@code response}. - * @param response the response to process - * @return the processed response - */ - OperationResponse preprocess(OperationResponse response); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationPreprocessorAdapter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationPreprocessorAdapter.java deleted file mode 100644 index ddfd7aa30..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationPreprocessorAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationResponse; - -/** - * An implementation of {@link OperationPreprocessor} that returns the request and - * response as-is. To be subclassed by preprocessor implementations that only modify the - * request or the response. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public abstract class OperationPreprocessorAdapter implements OperationPreprocessor { - - /** - * Returns the given {@code request} as-is. - * @param request the request - * @return the unmodified request - */ - @Override - public OperationRequest preprocess(OperationRequest request) { - return request; - } - - /** - * Returns the given {@code response} as-is. - * @param response the response - * @return the unmodified response - */ - @Override - public OperationResponse preprocess(OperationResponse response) { - return response; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationRequestPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationRequestPreprocessor.java deleted file mode 100644 index cfc38bd77..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationRequestPreprocessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import org.springframework.restdocs.operation.OperationRequest; - -/** - * An {@code OperationRequestPreprocessor} is used to modify an {@code OperationRequest} - * prior to it being documented. - * - * @author Andy Wilkinson - */ -public interface OperationRequestPreprocessor { - - /** - * Processes and potentially modifies the given {@code request} before it is - * documented. - * @param request the request - * @return the modified request - */ - OperationRequest preprocess(OperationRequest request); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationResponsePreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationResponsePreprocessor.java deleted file mode 100644 index 97f3400c6..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/OperationResponsePreprocessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import org.springframework.restdocs.operation.OperationResponse; - -/** - * An {@code OperationResponsePreprocessor} is used to modify an {@code OperationResponse} - * prior to it being documented. - * - * @author Andy Wilkinson - */ -public interface OperationResponsePreprocessor { - - /** - * Processes and potentially modifies the given {@code response} before it is - * documented. - * @param response the response - * @return the modified response - */ - OperationResponse preprocess(OperationResponse response); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PatternMatchHeaderFilter.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PatternMatchHeaderFilter.java deleted file mode 100644 index 6104bcfad..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PatternMatchHeaderFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * A {@link HeaderFilter} that excludes a header if its name matches a {@link Pattern}. - * - * @author Andy Wilkinson - * @author Roland Huss - */ -class PatternMatchHeaderFilter implements HeaderFilter { - - private Set exclusionPatterns; - - PatternMatchHeaderFilter(String... exclusionPatterns) { - this.exclusionPatterns = new HashSet<>(); - for (String exclusionPattern : exclusionPatterns) { - this.exclusionPatterns.add(Pattern.compile(exclusionPattern)); - } - } - - @Override - public boolean excludeHeader(String name) { - for (Pattern pattern : this.exclusionPatterns) { - if (pattern.matcher(name).matches()) { - return true; - } - } - return false; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifier.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifier.java deleted file mode 100644 index 1ac22beff..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifier.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.nio.charset.Charset; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.http.MediaType; - -/** - * A {@link ContentModifier} that modifies the content by replacing occurrences of a - * regular expression {@link Pattern}. - * - * @author Andy Wilkinson - * @author Dewet Diener - */ -class PatternReplacingContentModifier implements ContentModifier { - - private final Pattern pattern; - - private final String replacement; - - private final Charset fallbackCharset; - - /** - * Creates a new {@link PatternReplacingContentModifier} that will replace occurrences - * of the given {@code pattern} with the given {@code replacement}. The content is - * handled using the charset from its content type. When no content type is specified - * the JVM's {@link Charset#defaultCharset() default charset is used}. - * @param pattern the pattern - * @param replacement the replacement - */ - PatternReplacingContentModifier(Pattern pattern, String replacement) { - this(pattern, replacement, Charset.defaultCharset()); - } - - /** - * Creates a new {@link PatternReplacingContentModifier} that will replace occurrences - * of the given {@code pattern} with the given {@code replacement}. The content is - * handled using the charset from its content type. When no content type is specified - * the given {@code fallbackCharset} is used. - * @param pattern the pattern - * @param replacement the replacement - * @param fallbackCharset the charset to use as a fallback - */ - PatternReplacingContentModifier(Pattern pattern, String replacement, Charset fallbackCharset) { - this.pattern = pattern; - this.replacement = replacement; - this.fallbackCharset = fallbackCharset; - } - - @Override - public byte[] modifyContent(byte[] content, MediaType contentType) { - Charset charset = (contentType != null && contentType.getCharset() != null) ? contentType.getCharset() - : this.fallbackCharset; - String original = new String(content, charset); - Matcher matcher = this.pattern.matcher(original); - StringBuilder builder = new StringBuilder(); - int previous = 0; - while (matcher.find()) { - String prefix; - if (matcher.groupCount() > 0) { - prefix = original.substring(previous, matcher.start(1)); - previous = matcher.end(1); - } - else { - prefix = original.substring(previous, matcher.start()); - previous = matcher.end(); - } - builder.append(prefix); - builder.append(this.replacement); - } - if (previous < original.length()) { - builder.append(original.substring(previous)); - } - return builder.toString().getBytes(charset); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java deleted file mode 100644 index 6a36ed81e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.Arrays; -import java.util.regex.Pattern; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationResponse; - -/** - * Static factory methods for creating {@link OperationPreprocessor - * OperationPreprocessors} that can be applied to an {@link Operation Operation's} - * {@link OperationRequest request} or {@link OperationResponse response} before it is - * documented. - * - * @author Andy Wilkinson - * @author Roland Huss - * @author Jihoon Cha - */ -public final class Preprocessors { - - private Preprocessors() { - - } - - /** - * Returns an {@link OperationRequestPreprocessor} that will preprocess the request by - * applying the given {@code preprocessors} to it. - * @param preprocessors the preprocessors - * @return the request preprocessor - */ - public static OperationRequestPreprocessor preprocessRequest(OperationPreprocessor... preprocessors) { - return new DelegatingOperationRequestPreprocessor(Arrays.asList(preprocessors)); - } - - /** - * Returns an {@link OperationResponsePreprocessor} that will preprocess the response - * by applying the given {@code preprocessors} to it. - * @param preprocessors the preprocessors - * @return the response preprocessor - */ - public static OperationResponsePreprocessor preprocessResponse(OperationPreprocessor... preprocessors) { - return new DelegatingOperationResponsePreprocessor(Arrays.asList(preprocessors)); - } - - /** - * Returns an {@code OperationPreprocessor} that will pretty print the content of the - * request or response. - * @return the preprocessor - */ - public static OperationPreprocessor prettyPrint() { - return new ContentModifyingOperationPreprocessor(new PrettyPrintingContentModifier()); - } - - /** - * Returns an {@code OperationPreprocessor} that will mask the href of hypermedia - * links in the request or response. - * @return the preprocessor - */ - public static OperationPreprocessor maskLinks() { - return new ContentModifyingOperationPreprocessor(new LinkMaskingContentModifier()); - } - - /** - * Returns an {@code OperationPreprocessor} that will mask the href of hypermedia - * links in the request or response. - * @param mask the link mask - * @return the preprocessor - */ - public static OperationPreprocessor maskLinks(String mask) { - return new ContentModifyingOperationPreprocessor(new LinkMaskingContentModifier(mask)); - } - - /** - * Returns an {@code OperationPreprocessor} that will modify the content of the - * request or response by replacing occurrences of the given {@code pattern} with the - * given {@code replacement}. - * @param pattern the pattern - * @param replacement the replacement - * @return the preprocessor - */ - public static OperationPreprocessor replacePattern(Pattern pattern, String replacement) { - return new ContentModifyingOperationPreprocessor(new PatternReplacingContentModifier(pattern, replacement)); - } - - /** - * Returns a {@code HeadersModifyingOperationPreprocessor} that can then be configured - * to modify the headers of the request or response. - * @return the preprocessor - * @since 3.0.0 - */ - public static HeadersModifyingOperationPreprocessor modifyHeaders() { - return new HeadersModifyingOperationPreprocessor(); - } - - /** - * Returns a {@code UriModifyingOperationPreprocessor} that will modify URIs in the - * request or response by changing one or more of their host, scheme, and port. - * @return the preprocessor - * @since 2.0.1 - */ - public static UriModifyingOperationPreprocessor modifyUris() { - return new UriModifyingOperationPreprocessor(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifier.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifier.java deleted file mode 100644 index fe0105553..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifier.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.ErrorListener; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.stream.StreamResult; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; - -import org.springframework.http.MediaType; - -/** - * A {@link ContentModifier} that modifies the content by pretty printing it. - * - * @author Andy Wilkinson - */ -public class PrettyPrintingContentModifier implements ContentModifier { - - private static final List PRETTY_PRINTERS = Collections - .unmodifiableList(Arrays.asList(new JsonPrettyPrinter(), new XmlPrettyPrinter())); - - @Override - public byte[] modifyContent(byte[] originalContent, MediaType contentType) { - if (originalContent.length > 0) { - for (PrettyPrinter prettyPrinter : PRETTY_PRINTERS) { - try { - return prettyPrinter.prettyPrint(originalContent); - } - catch (Exception ex) { - // Continue - } - } - } - return originalContent; - } - - private interface PrettyPrinter { - - byte[] prettyPrint(byte[] content) throws Exception; - - } - - private static final class XmlPrettyPrinter implements PrettyPrinter { - - @Override - public byte[] prettyPrint(byte[] original) throws Exception { - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes"); - ByteArrayOutputStream transformed = new ByteArrayOutputStream(); - transformer.setErrorListener(new SilentErrorListener()); - transformer.transform(createSaxSource(original), new StreamResult(transformed)); - - return transformed.toByteArray(); - } - - private SAXSource createSaxSource(byte[] original) throws ParserConfigurationException, SAXException { - SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - SAXParser parser = parserFactory.newSAXParser(); - XMLReader xmlReader = parser.getXMLReader(); - xmlReader.setErrorHandler(new SilentErrorHandler()); - return new SAXSource(xmlReader, new InputSource(new ByteArrayInputStream(original))); - } - - private static final class SilentErrorListener implements ErrorListener { - - @Override - public void warning(TransformerException exception) throws TransformerException { - // Suppress - } - - @Override - public void error(TransformerException exception) throws TransformerException { - // Suppress - } - - @Override - public void fatalError(TransformerException exception) throws TransformerException { - // Suppress - } - - } - - private static final class SilentErrorHandler implements ErrorHandler { - - @Override - public void warning(SAXParseException exception) throws SAXException { - // Suppress - } - - @Override - public void error(SAXParseException exception) throws SAXException { - // Suppress - } - - @Override - public void fatalError(SAXParseException exception) throws SAXException { - // Suppress - } - - } - - } - - private static final class JsonPrettyPrinter implements PrettyPrinter { - - private final ObjectMapper objectMapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true) - .configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true); - - @Override - public byte[] prettyPrint(byte[] original) throws IOException { - return this.objectMapper.writeValueAsBytes(this.objectMapper.readTree(original)); - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java deleted file mode 100644 index 6e15b74b9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessor.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationRequestPartFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * An {@link OperationPreprocessor} that modifies URIs in the request and in the response - * by changing one or more of their host, scheme, and port. URIs in the following - * locations are modified: - *
    - *
  • {@link OperationRequest#getUri() Request URI} - *
  • {@link OperationRequest#getHeaders() Request headers} - *
  • {@link OperationRequest#getContent() Request content} - *
  • {@link OperationRequestPart#getHeaders() Request part headers} - *
  • {@link OperationRequestPart#getContent() Request part content} - *
  • {@link OperationResponse#getHeaders() Response headers} - *
  • {@link OperationResponse#getContent() Response content} - *
- * - * @author Andy Wilkinson - * @since 2.0.1 - */ -public class UriModifyingOperationPreprocessor implements OperationPreprocessor { - - private final UriModifyingContentModifier contentModifier = new UriModifyingContentModifier(); - - private final OperationPreprocessor contentModifyingDelegate = new ContentModifyingOperationPreprocessor( - this.contentModifier); - - private String scheme; - - private String host; - - private String port; - - /** - * Modifies the URI to use the given {@code scheme}. {@code null}, the default, will - * leave the scheme unchanged. - * @param scheme the scheme - * @return {@code this} - */ - public UriModifyingOperationPreprocessor scheme(String scheme) { - this.scheme = scheme; - this.contentModifier.setScheme(scheme); - return this; - } - - /** - * Modifies the URI to use the given {@code host}. {@code null}, the default, will - * leave the host unchanged. - * @param host the host - * @return {@code this} - */ - public UriModifyingOperationPreprocessor host(String host) { - this.host = host; - this.contentModifier.setHost(host); - return this; - } - - /** - * Modifies the URI to use the given {@code port}. - * @param port the port - * @return {@code this} - */ - public UriModifyingOperationPreprocessor port(int port) { - return port(Integer.toString(port)); - } - - /** - * Removes the port from the URI. - * @return {@code this} - */ - public UriModifyingOperationPreprocessor removePort() { - return port(""); - } - - private UriModifyingOperationPreprocessor port(String port) { - this.port = port; - this.contentModifier.setPort(port); - return this; - } - - @Override - public OperationRequest preprocess(OperationRequest request) { - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(request.getUri()); - if (this.scheme != null) { - uriBuilder.scheme(this.scheme); - } - if (this.host != null) { - uriBuilder.host(this.host); - } - if (this.port != null) { - if (StringUtils.hasText(this.port)) { - uriBuilder.port(this.port); - } - else { - uriBuilder.port(null); - } - } - URI modifiedUri = uriBuilder.build(true).toUri(); - HttpHeaders modifiedHeaders = modify(request.getHeaders()); - modifiedHeaders.set(HttpHeaders.HOST, - modifiedUri.getHost() + ((modifiedUri.getPort() != -1) ? ":" + modifiedUri.getPort() : "")); - return this.contentModifyingDelegate - .preprocess(new OperationRequestFactory().create(uriBuilder.build(true).toUri(), request.getMethod(), - request.getContent(), modifiedHeaders, modify(request.getParts()), request.getCookies())); - } - - @Override - public OperationResponse preprocess(OperationResponse response) { - return this.contentModifyingDelegate.preprocess(new OperationResponseFactory().create(response.getStatus(), - modify(response.getHeaders()), response.getContent(), response.getCookies())); - } - - private HttpHeaders modify(HttpHeaders headers) { - HttpHeaders modified = new HttpHeaders(); - for (Entry> header : headers.headerSet()) { - for (String value : header.getValue()) { - modified.add(header.getKey(), this.contentModifier.modify(value)); - } - } - return modified; - } - - private Collection modify(Collection parts) { - List modifiedParts = new ArrayList<>(); - OperationRequestPartFactory factory = new OperationRequestPartFactory(); - for (OperationRequestPart part : parts) { - modifiedParts.add(factory.create(part.getName(), part.getSubmittedFileName(), - this.contentModifier.modifyContent(part.getContent(), part.getHeaders().getContentType()), - modify(part.getHeaders()))); - } - return modifiedParts; - } - - private static final class UriModifyingContentModifier implements ContentModifier { - - private static final Pattern SCHEME_HOST_PORT_PATTERN = Pattern - .compile("(http[s]?)://([a-zA-Z0-9-\\.]+)(:[0-9]+)?"); - - private String scheme; - - private String host; - - private String port; - - private void setScheme(String scheme) { - this.scheme = scheme; - } - - private void setHost(String host) { - this.host = host; - } - - private void setPort(String port) { - this.port = port; - } - - @Override - public byte[] modifyContent(byte[] content, MediaType contentType) { - String input; - if (contentType != null && contentType.getCharset() != null) { - input = new String(content, contentType.getCharset()); - } - else { - input = new String(content); - } - - return modify(input).getBytes(); - } - - private String modify(String input) { - List replacements = Arrays.asList(this.scheme, this.host, - StringUtils.hasText(this.port) ? ":" + this.port : this.port); - - int previous = 0; - - Matcher matcher = SCHEME_HOST_PORT_PATTERN.matcher(input); - StringBuilder builder = new StringBuilder(); - while (matcher.find()) { - for (int i = 1; i <= matcher.groupCount(); i++) { - if (matcher.start(i) >= 0) { - builder.append(input, previous, matcher.start(i)); - } - if (matcher.start(i) >= 0) { - previous = matcher.end(i); - } - builder.append(getReplacement(matcher.group(i), replacements.get(i - 1))); - } - } - - if (previous < input.length()) { - builder.append(input.substring(previous)); - } - return builder.toString(); - } - - private String getReplacement(String original, String candidate) { - if (candidate != null) { - return candidate; - } - if (original != null) { - return original; - } - return ""; - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/package-info.java deleted file mode 100644 index 8f1bf78ea..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Support for preprocessing an operation prior to it being documented. - */ -package org.springframework.restdocs.operation.preprocess; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/package-info.java deleted file mode 100644 index 540f8a207..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Core Spring REST Docs classes. - */ -package org.springframework.restdocs; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractBodySnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractBodySnippet.java deleted file mode 100644 index 3ccd56881..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractBodySnippet.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.ModelCreationException; -import org.springframework.restdocs.snippet.TemplatedSnippet; - -/** - * Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that - * document a RESTful resource's request or response body. - * - * @author Andy Wilkinson - * @author Achim Grimm - */ -public abstract class AbstractBodySnippet extends TemplatedSnippet { - - private final PayloadSubsectionExtractor subsectionExtractor; - - /** - * Creates a new {@code AbstractBodySnippet} that will produce a snippet named - * {@code -body} using a template named {@code -body}. The snippet will - * contain the subsection of the body extracted by the given - * {@code subsectionExtractor}. The given {@code attributes} will be included in the - * model during template rendering - * @param type the type of the body - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - */ - protected AbstractBodySnippet(String type, PayloadSubsectionExtractor subsectionExtractor, - Map attributes) { - this(type, type, subsectionExtractor, attributes); - } - - /** - * Creates a new {@code AbstractBodySnippet} that will produce a snippet named - * {@code -body} using a template named {@code -body}. The snippet will - * contain the subsection of the body extracted by the given - * {@code subsectionExtractor}. The given {@code attributes} will be included in the - * model during template rendering - * @param name the name of the snippet - * @param type the type of the body - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - */ - protected AbstractBodySnippet(String name, String type, PayloadSubsectionExtractor subsectionExtractor, - Map attributes) { - super(name + "-body" + ((subsectionExtractor != null) ? "-" + subsectionExtractor.getSubsectionId() : ""), - type + "-body", attributes); - this.subsectionExtractor = subsectionExtractor; - } - - @Override - protected Map createModel(Operation operation) { - try { - MediaType contentType = getContentType(operation); - String language = determineLanguage(contentType); - byte[] content = getContent(operation); - if (this.subsectionExtractor != null) { - content = this.subsectionExtractor.extractSubsection(content, contentType); - } - Charset charset = extractCharset(contentType); - String body = (charset != null) ? new String(content, charset) : new String(content); - Map model = new HashMap<>(); - model.put("language", language); - model.put("body", body); - return model; - } - catch (IOException ex) { - throw new ModelCreationException(ex); - } - } - - private String determineLanguage(MediaType contentType) { - if (contentType == null) { - return null; - } - return (contentType.getSubtypeSuffix() != null) ? contentType.getSubtypeSuffix() : contentType.getSubtype(); - } - - private Charset extractCharset(MediaType contentType) { - if (contentType == null) { - return null; - } - return contentType.getCharset(); - } - - /** - * Returns the content of the request or response extracted from the given - * {@code operation}. - * @param operation the operation - * @return the content - * @throws IOException if the content cannot be extracted - */ - protected abstract byte[] getContent(Operation operation) throws IOException; - - /** - * Returns the content type of the request or response extracted from the given - * {@code operation}. - * @param operation the operation - * @return the content type - */ - protected abstract MediaType getContentType(Operation operation); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java deleted file mode 100644 index fe077017e..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/AbstractFieldsSnippet.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Attributes; -import org.springframework.restdocs.snippet.Attributes.Attribute; -import org.springframework.restdocs.snippet.ModelCreationException; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that - * document a RESTful resource's request or response fields. - * - * @author Andreas Evers - * @author Andy Wilkinson - * @author Mathias Düsterhöft - */ -public abstract class AbstractFieldsSnippet extends TemplatedSnippet { - - private final List fieldDescriptors; - - private final boolean ignoreUndocumentedFields; - - private final String type; - - private final PayloadSubsectionExtractor subsectionExtractor; - - /** - * Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named - * {@code -fields} using a template named {@code -fields}. The fields will - * be documented using the given {@code descriptors} and the given {@code attributes} - * will be included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param type the type of the fields - * @param descriptors the field descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected AbstractFieldsSnippet(String type, List descriptors, Map attributes, - boolean ignoreUndocumentedFields) { - this(type, type, descriptors, attributes, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named - * {@code -fields} using a template named {@code -fields}. The fields in - * the subsection of the payload extracted by the given {@code subsectionExtractor} - * will be documented using the given {@code descriptors} and the given - * {@code attributes} will be included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param type the type of the fields - * @param descriptors the field descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - * @param subsectionExtractor the subsection extractor - * @since 1.2.0 - */ - protected AbstractFieldsSnippet(String type, List descriptors, Map attributes, - boolean ignoreUndocumentedFields, PayloadSubsectionExtractor subsectionExtractor) { - this(type, type, descriptors, attributes, ignoreUndocumentedFields, subsectionExtractor); - } - - /** - * Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named - * {@code -fields} using a template named {@code -fields}. The fields will - * be documented using the given {@code descriptors} and the given {@code attributes} - * will be included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param name the name of the snippet - * @param type the type of the fields - * @param descriptors the field descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected AbstractFieldsSnippet(String name, String type, List descriptors, - Map attributes, boolean ignoreUndocumentedFields) { - this(name, type, descriptors, attributes, ignoreUndocumentedFields, null); - } - - /** - * Creates a new {@code AbstractFieldsSnippet} that will produce a snippet named - * {@code -fields} using a template named {@code -fields}. The fields in - * the subsection of the payload identified by {@code subsectionPath} will be - * documented using the given {@code descriptors} and the given {@code attributes} - * will be included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param name the name of the snippet - * @param type the type of the fields - * @param descriptors the field descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - * @param subsectionExtractor the subsection extractor documented. {@code null} or an - * empty string can be used to indicate that the entire payload should be documented. - * @since 1.2.0 - */ - protected AbstractFieldsSnippet(String name, String type, List descriptors, - Map attributes, boolean ignoreUndocumentedFields, - PayloadSubsectionExtractor subsectionExtractor) { - super(name + "-fields" + ((subsectionExtractor != null) ? "-" + subsectionExtractor.getSubsectionId() : ""), - type + "-fields", attributes); - for (FieldDescriptor descriptor : descriptors) { - Assert.notNull(descriptor.getPath(), "Field descriptors must have a path"); - if (!descriptor.isIgnored()) { - Assert.notNull(descriptor.getDescription() != null, "The descriptor for '" + descriptor.getPath() - + "' must have a" + " description or it must be marked as ignored"); - } - } - this.fieldDescriptors = descriptors; - this.ignoreUndocumentedFields = ignoreUndocumentedFields; - this.type = type; - this.subsectionExtractor = subsectionExtractor; - } - - @Override - protected Map createModel(Operation operation) { - byte[] content; - try { - content = verifyContent(getContent(operation)); - } - catch (IOException ex) { - throw new ModelCreationException(ex); - } - MediaType contentType = getContentType(operation); - if (this.subsectionExtractor != null) { - content = verifyContent( - this.subsectionExtractor.extractSubsection(content, contentType, this.fieldDescriptors)); - } - ContentHandler contentHandler = ContentHandler.forContentWithDescriptors(content, contentType, - this.fieldDescriptors); - - validateFieldDocumentation(contentHandler); - - List descriptorsToDocument = new ArrayList<>(); - for (FieldDescriptor descriptor : this.fieldDescriptors) { - if (!descriptor.isIgnored()) { - try { - Object type = contentHandler.resolveFieldType(descriptor); - descriptorsToDocument.add(copyWithType(descriptor, type)); - } - catch (FieldDoesNotExistException ex) { - String message = "Cannot determine the type of the field '" + descriptor.getPath() - + "' as it is not present in the " + "payload. Please provide a type using " - + "FieldDescriptor.type(Object type)."; - throw new FieldTypeRequiredException(message); - } - } - } - - Map model = new HashMap<>(); - List> fields = new ArrayList<>(); - model.put("fields", fields); - for (FieldDescriptor descriptor : descriptorsToDocument) { - if (!descriptor.isIgnored()) { - fields.add(createModelForDescriptor(descriptor)); - } - } - return model; - } - - private byte[] verifyContent(byte[] content) { - if (content.length == 0) { - throw new SnippetException( - "Cannot document " + this.type + " fields as the " + this.type + " body is empty"); - } - return content; - } - - private void validateFieldDocumentation(ContentHandler payloadHandler) { - List missingFields = payloadHandler.findMissingFields(); - - String undocumentedPayload = this.ignoreUndocumentedFields ? null : payloadHandler.getUndocumentedContent(); - - if (!missingFields.isEmpty() || StringUtils.hasText(undocumentedPayload)) { - String message = ""; - if (StringUtils.hasText(undocumentedPayload)) { - message += String.format("The following parts of the payload were" + " not documented:%n%s", - undocumentedPayload); - } - if (!missingFields.isEmpty()) { - if (message.length() > 0) { - message += String.format("%n"); - } - List paths = new ArrayList<>(); - for (FieldDescriptor fieldDescriptor : missingFields) { - paths.add(fieldDescriptor.getPath()); - } - message += "Fields with the following paths were not found in the" + " payload: " + paths; - } - throw new SnippetException(message); - } - } - - /** - * Returns the content type of the request or response extracted from the given - * {@code operation}. - * @param operation the operation - * @return the content type - */ - protected abstract MediaType getContentType(Operation operation); - - /** - * Returns the content of the request or response extracted form the given - * {@code operation}. - * @param operation the operation - * @return the content - * @throws IOException if the content cannot be extracted - */ - protected abstract byte[] getContent(Operation operation) throws IOException; - - /** - * Returns the list of {@link FieldDescriptor FieldDescriptors} that will be used to - * generate the documentation. - * @return the field descriptors - */ - protected final List getFieldDescriptors() { - return this.fieldDescriptors; - } - - /** - * Returns whether or not this snippet ignores undocumented fields. - * @return {@code true} if undocumented fields are ignored, otherwise {@code false} - */ - protected final boolean isIgnoredUndocumentedFields() { - return this.ignoreUndocumentedFields; - } - - /** - * Returns the {@link PayloadSubsectionExtractor}, if any, used by this snippet. - * @return the subsection extractor or {@code null} - * @since 1.2.4 - */ - protected final PayloadSubsectionExtractor getSubsectionExtractor() { - return this.subsectionExtractor; - } - - /** - * Returns a model for the given {@code descriptor}. - * @param descriptor the descriptor - * @return the model - */ - protected Map createModelForDescriptor(FieldDescriptor descriptor) { - Map model = new HashMap<>(); - model.put("path", descriptor.getPath()); - model.put("type", descriptor.getType().toString()); - model.put("description", descriptor.getDescription()); - model.put("optional", descriptor.isOptional()); - model.putAll(descriptor.getAttributes()); - return model; - } - - private FieldDescriptor copyWithType(FieldDescriptor source, Object type) { - FieldDescriptor result = (source instanceof SubsectionDescriptor) ? new SubsectionDescriptor(source.getPath()) - : new FieldDescriptor(source.getPath()); - result.description(source.getDescription()).type(type).attributes(asArray(source.getAttributes())); - if (source.isIgnored()) { - result.ignored(); - } - if (source.isOptional()) { - result.optional(); - } - return result; - } - - private static Attribute[] asArray(Map attributeMap) { - List attributes = new ArrayList<>(); - for (Map.Entry attribute : attributeMap.entrySet()) { - attributes.add(Attributes.key(attribute.getKey()).value(attribute.getValue())); - } - return attributes.toArray(new Attribute[attributes.size()]); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java deleted file mode 100644 index 0111548c3..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ContentHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.List; - -import org.springframework.http.MediaType; - -/** - * A handler for the content of a request or response. - * - * @author Andy Wilkinson - * @author Mathias Düsterhöft - */ -interface ContentHandler extends FieldTypeResolver { - - /** - * Finds the fields that are missing from the handler's payload. A field is missing if - * it is described but is not present in the payload. - * @return descriptors for the fields that are missing from the payload - * @throws PayloadHandlingException if a failure occurs - */ - List findMissingFields(); - - /** - * Returns modified content, formatted as a String, that only contains the fields that - * are undocumented. A field is undocumented if it is present in the handler's content - * but is not described. If the content is completely documented, {@code null} is - * returned - * @return the undocumented content, or {@code null} if all of the content is - * documented - * @throws PayloadHandlingException if a failure occurs - */ - String getUndocumentedContent(); - - /** - * Create a {@link ContentHandler} for the given content type and payload, described - * by the given descriptors. - * @param content the payload - * @param contentType the content type - * @param descriptors descriptors of the content - * @return the ContentHandler - * @throws PayloadHandlingException if no known ContentHandler can handle the content - */ - static ContentHandler forContentWithDescriptors(byte[] content, MediaType contentType, - List descriptors) { - try { - return new JsonContentHandler(content, descriptors); - } - catch (Exception je) { - try { - return new XmlContentHandler(content, descriptors); - } - catch (Exception xe) { - throw new PayloadHandlingException( - "Cannot handle " + contentType + " content as it could not be parsed as JSON or XML"); - } - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldDescriptor.java deleted file mode 100644 index 3a58b57d9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldDescriptor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import org.springframework.restdocs.snippet.IgnorableDescriptor; - -/** - * A description of a field found in a request or response payload. - * - * @author Andreas Evers - * @author Andy Wilkinson - * @see PayloadDocumentation#fieldWithPath(String) - */ -public class FieldDescriptor extends IgnorableDescriptor { - - private final String path; - - private Object type; - - private boolean optional; - - /** - * Creates a new {@code FieldDescriptor} describing the field with the given - * {@code path}. - * @param path the path - */ - protected FieldDescriptor(String path) { - this.path = path; - } - - /** - * Specifies the type of the field. When documenting a JSON payload, the - * {@link JsonFieldType} enumeration will typically be used. - * @param type the type of the field - * @return {@code this} - * @see JsonFieldType - */ - public final FieldDescriptor type(Object type) { - this.type = type; - return this; - } - - /** - * Marks the field as optional. - * @return {@code this} - */ - public final FieldDescriptor optional() { - this.optional = true; - return this; - } - - /** - * Returns the path of the field described by this descriptor. - * @return the path - */ - public final String getPath() { - return this.path; - } - - /** - * Returns the type of the field described by this descriptor. - * @return the type - */ - public final Object getType() { - return this.type; - } - - /** - * Returns {@code true} if the described field is optional, otherwise {@code false}. - * @return {@code true} if the described field is optional, otherwise {@code false} - */ - public final boolean isOptional() { - return this.optional; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldDoesNotExistException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldDoesNotExistException.java deleted file mode 100644 index 1df61f1a4..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldDoesNotExistException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -/** - * A {@code FieldDoesNotExistException} is thrown when a requested field does not exist in - * a payload. - * - * @author Andy Wilkinson - */ -@SuppressWarnings("serial") -public class FieldDoesNotExistException extends RuntimeException { - - /** - * Creates a new {@code FieldDoesNotExistException} that indicates that the field with - * the given {@code fieldPath} does not exist. - * @param fieldPath the path of the field that does not exist - */ - public FieldDoesNotExistException(String fieldPath) { - super("The payload does not contain a field with the path '" + fieldPath + "'"); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractor.java deleted file mode 100644 index 1315ce810..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractor.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; - -/** - * A {@link PayloadSubsectionExtractor} that extracts the subsection of the JSON payload - * identified by a field path. - * - * @author Andy Wilkinson - * @since 1.2.0 - * @see PayloadDocumentation#beneathPath(String) - */ -public class FieldPathPayloadSubsectionExtractor - implements PayloadSubsectionExtractor { - - private static final ObjectMapper objectMapper = new ObjectMapper(); - - private static final ObjectMapper prettyPrintingOjectMapper = new ObjectMapper() - .enable(SerializationFeature.INDENT_OUTPUT); - - private final String fieldPath; - - private final String subsectionId; - - /** - * Creates a new {@code FieldPathPayloadSubsectionExtractor} that will extract the - * subsection of the JSON payload beneath the given {@code fieldPath}. The - * {@code fieldPath} prefixed with {@code beneath-} with be used as the subsection ID. - * @param fieldPath the path of the field - */ - protected FieldPathPayloadSubsectionExtractor(String fieldPath) { - this(fieldPath, "beneath-" + fieldPath); - } - - /** - * Creates a new {@code FieldPathPayloadSubsectionExtractor} that will extract the - * subsection of the JSON payload beneath the given {@code fieldPath} and that will - * use the given {@code subsectionId} to identify the subsection. - * @param fieldPath the path of the field - * @param subsectionId the ID of the subsection - */ - protected FieldPathPayloadSubsectionExtractor(String fieldPath, String subsectionId) { - this.fieldPath = fieldPath; - this.subsectionId = subsectionId; - } - - @Override - public byte[] extractSubsection(byte[] payload, MediaType contentType) { - return extractSubsection(payload, contentType, Collections.emptyList()); - } - - @Override - public byte[] extractSubsection(byte[] payload, MediaType contentType, List descriptors) { - try { - ExtractedField extractedField = new JsonFieldProcessor().extract(this.fieldPath, - objectMapper.readValue(payload, Object.class)); - Object value = extractedField.getValue(); - if (value == ExtractedField.ABSENT) { - throw new PayloadHandlingException(this.fieldPath + " does not identify a section of the payload"); - } - Map descriptorsByPath = descriptors.stream() - .collect(Collectors.toMap( - (descriptor) -> JsonFieldPath.compile(this.fieldPath + "." + descriptor.getPath()), - this::prependFieldPath)); - if (value instanceof List) { - List extractedList = (List) value; - if (extractedList.isEmpty()) { - throw new PayloadHandlingException(this.fieldPath + " identifies an empty section of the payload"); - } - JsonContentHandler contentHandler = new JsonContentHandler(payload, descriptorsByPath.values()); - Set uncommonPaths = JsonFieldPaths.from(extractedList) - .getUncommon() - .stream() - .map((path) -> JsonFieldPath - .compile((path.equals("")) ? this.fieldPath : this.fieldPath + "." + path)) - .filter((path) -> { - FieldDescriptor descriptorForPath = descriptorsByPath.getOrDefault(path, - new FieldDescriptor(path.toString())); - return contentHandler.isMissing(descriptorForPath); - }) - .collect(Collectors.toSet()); - if (uncommonPaths.isEmpty()) { - value = extractedList.get(0); - } - else { - String message = this.fieldPath + " identifies multiple sections of " - + "the payload and they do not have a common structure. The " - + "following non-optional uncommon paths were found: "; - message += uncommonPaths.stream() - .map(JsonFieldPath::toString) - .collect(Collectors.toCollection(TreeSet::new)); - throw new PayloadHandlingException(message); - } - } - return getObjectMapper(payload).writeValueAsBytes(value); - } - catch (IOException ex) { - throw new PayloadHandlingException(ex); - } - } - - private FieldDescriptor prependFieldPath(FieldDescriptor original) { - FieldDescriptor prefixed = new FieldDescriptor(this.fieldPath + "." + original.getPath()); - if (original.isOptional()) { - prefixed.optional(); - } - return prefixed; - } - - @Override - public String getSubsectionId() { - return this.subsectionId; - } - - /** - * Returns the path of the field that will be extracted. - * @return the path of the field - */ - protected String getFieldPath() { - return this.fieldPath; - } - - @Override - public FieldPathPayloadSubsectionExtractor withSubsectionId(String subsectionId) { - return new FieldPathPayloadSubsectionExtractor(this.fieldPath, subsectionId); - } - - private ObjectMapper getObjectMapper(byte[] payload) { - if (isPrettyPrinted(payload)) { - return prettyPrintingOjectMapper; - } - return objectMapper; - } - - private boolean isPrettyPrinted(byte[] payload) { - for (byte b : payload) { - if (b == '\n') { - return true; - } - } - return false; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeRequiredException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeRequiredException.java deleted file mode 100644 index 6b2afd898..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeRequiredException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -/** - * A {@code FieldTypeRequiredException} is thrown when a field's type cannot be determined - * automatically and, therefore, must be explicitly provided. - * - * @author Andy Wilkinson - */ -@SuppressWarnings("serial") -public class FieldTypeRequiredException extends RuntimeException { - - /** - * Creates a new {@code FieldTypeRequiredException} indicating that a type is required - * for the reason described in the given {@code message}. - * @param message the message - */ - public FieldTypeRequiredException(String message) { - super(message); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java deleted file mode 100644 index 7f574a9e9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypeResolver.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.List; - -import org.springframework.http.MediaType; - -/** - * Resolves the type of a field in a request or response payload. - * - * @author Mathias Düsterhöft - * @author Andy Wilkinson - * @since 2.0.3 - */ -public interface FieldTypeResolver { - - /** - * Create a {@code FieldTypeResolver} for the given {@code content} and - * {@code contentType}, described by the given {@code descriptors}. - * @param content the payload that the {@code FieldTypeResolver} should handle - * @param contentType the content type of the payload - * @param descriptors the descriptors of the content - * @return the {@code FieldTypeResolver} - */ - static FieldTypeResolver forContentWithDescriptors(byte[] content, MediaType contentType, - List descriptors) { - return ContentHandler.forContentWithDescriptors(content, contentType, descriptors); - } - - /** - * Resolves the type of the field that is described by the given - * {@code fieldDescriptor} based on the content of the payload. - * @param fieldDescriptor the field descriptor - * @return the type of the field - */ - Object resolveFieldType(FieldDescriptor fieldDescriptor); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypesDoNotMatchException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypesDoNotMatchException.java deleted file mode 100644 index 477a1900b..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/FieldTypesDoNotMatchException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -/** - * A {@code FieldTypesDoNotMatchException} is thrown when the documented and actual types - * of a field do not match. - * - * @author Andy Wilkinson - */ -class FieldTypesDoNotMatchException extends RuntimeException { - - /** - * Creates a new {@code FieldTypesDoNotMatchException} for the field described by the - * given {@code fieldDescriptor} that has the given {@code actualType}. - * @param fieldDescriptor the field - * @param actualType the actual type of the field - */ - FieldTypesDoNotMatchException(FieldDescriptor fieldDescriptor, Object actualType) { - super("The documented type of the field '" + fieldDescriptor.getPath() + "' is " + fieldDescriptor.getType() - + " but the actual type is " + actualType); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java deleted file mode 100644 index 52a8d1d48..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonContentHandler.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; - -import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; - -/** - * A {@link ContentHandler} for JSON content. - * - * @author Andy Wilkinson - * @author Mathias Düsterhöft - */ -class JsonContentHandler implements ContentHandler { - - private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor(); - - private final JsonFieldTypesDiscoverer fieldTypesDiscoverer = new JsonFieldTypesDiscoverer(); - - private final ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - - private final byte[] rawContent; - - private final Collection fieldDescriptors; - - JsonContentHandler(byte[] content, Collection fieldDescriptors) { - this.rawContent = content; - this.fieldDescriptors = fieldDescriptors; - readContent(); - } - - @Override - public List findMissingFields() { - List missingFields = new ArrayList<>(); - for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) { - if (isMissing(fieldDescriptor)) { - missingFields.add(fieldDescriptor); - } - } - - return missingFields; - } - - boolean isMissing(FieldDescriptor descriptor) { - Object payload = readContent(); - return !descriptor.isOptional() && !this.fieldProcessor.hasField(descriptor.getPath(), payload) - && !isNestedBeneathMissingOptionalField(descriptor, payload); - } - - private boolean isNestedBeneathMissingOptionalField(FieldDescriptor descriptor, Object payload) { - List candidates = new ArrayList<>(this.fieldDescriptors); - candidates.remove(descriptor); - for (FieldDescriptor candidate : candidates) { - if (candidate.isOptional() && descriptor.getPath().startsWith(candidate.getPath()) - && isMissing(candidate, payload)) { - return true; - } - } - return false; - } - - private boolean isMissing(FieldDescriptor candidate, Object payload) { - if (!this.fieldProcessor.hasField(candidate.getPath(), payload)) { - return true; - } - ExtractedField extracted = this.fieldProcessor.extract(candidate.getPath(), payload); - return extracted.getValue() == null || isEmptyCollection(extracted.getValue()); - } - - private boolean isEmptyCollection(Object value) { - if (!(value instanceof Collection)) { - return false; - } - Collection collection = (Collection) value; - for (Object entry : collection) { - if (!isEmptyCollection(entry)) { - return false; - } - } - return true; - } - - @Override - public String getUndocumentedContent() { - Object content = readContent(); - for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) { - if (describesSubsection(fieldDescriptor)) { - this.fieldProcessor.removeSubsection(fieldDescriptor.getPath(), content); - } - else { - this.fieldProcessor.remove(fieldDescriptor.getPath(), content); - } - } - if (!isEmpty(content)) { - try { - return this.objectMapper.writeValueAsString(content); - } - catch (JsonProcessingException ex) { - throw new PayloadHandlingException(ex); - } - } - return null; - } - - private boolean describesSubsection(FieldDescriptor fieldDescriptor) { - return fieldDescriptor instanceof SubsectionDescriptor; - } - - private Object readContent() { - try { - return new ObjectMapper().readValue(this.rawContent, Object.class); - } - catch (IOException ex) { - throw new PayloadHandlingException(ex); - } - } - - private boolean isEmpty(Object object) { - if (object instanceof Map) { - return ((Map) object).isEmpty(); - } - return ((List) object).isEmpty(); - } - - @Override - public Object resolveFieldType(FieldDescriptor fieldDescriptor) { - if (fieldDescriptor.getType() == null) { - return this.fieldTypesDiscoverer.discoverFieldTypes(fieldDescriptor.getPath(), readContent()) - .coalesce(fieldDescriptor.isOptional()); - } - if (!(fieldDescriptor.getType() instanceof JsonFieldType)) { - return fieldDescriptor.getType(); - } - JsonFieldType descriptorFieldType = (JsonFieldType) fieldDescriptor.getType(); - try { - JsonFieldType actualFieldType = this.fieldTypesDiscoverer - .discoverFieldTypes(fieldDescriptor.getPath(), readContent()) - .coalesce(fieldDescriptor.isOptional()); - if (descriptorFieldType == JsonFieldType.VARIES || descriptorFieldType == actualFieldType - || (fieldDescriptor.isOptional() && actualFieldType == JsonFieldType.NULL) - || (isNestedBeneathMissingOptionalField(fieldDescriptor, readContent()) - && actualFieldType == JsonFieldType.VARIES)) { - return descriptorFieldType; - } - throw new FieldTypesDoNotMatchException(fieldDescriptor, actualFieldType); - } - catch (FieldDoesNotExistException ex) { - return fieldDescriptor.getType(); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java deleted file mode 100644 index 9e7610475..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPath.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2014-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A path that identifies a field in a JSON payload. - * - * @author Andy Wilkinson - * @author Jeremy Rickard - */ -final class JsonFieldPath { - - private static final Pattern BRACKETS_AND_ARRAY_PATTERN = Pattern - .compile("\\[\'(.+?)\'\\]|\\[([0-9]+|\\*){0,1}\\]"); - - private static final Pattern ARRAY_INDEX_PATTERN = Pattern.compile("\\[([0-9]+|\\*){0,1}\\]"); - - private final String rawPath; - - private final List segments; - - private final PathType type; - - private JsonFieldPath(String rawPath, List segments, PathType type) { - this.rawPath = rawPath; - this.segments = segments; - this.type = type; - } - - PathType getType() { - return this.type; - } - - List getSegments() { - return this.segments; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - JsonFieldPath other = (JsonFieldPath) obj; - return this.segments.equals(other.segments); - } - - @Override - public int hashCode() { - return this.segments.hashCode(); - } - - @Override - public String toString() { - return this.rawPath; - } - - static JsonFieldPath compile(String path) { - List segments = extractSegments(path); - return new JsonFieldPath(path, segments, matchesSingleValue(segments) ? PathType.SINGLE : PathType.MULTI); - } - - static boolean isArraySegment(String segment) { - return ARRAY_INDEX_PATTERN.matcher(segment).matches(); - } - - static boolean matchesSingleValue(List segments) { - Iterator iterator = segments.iterator(); - while (iterator.hasNext()) { - String segment = iterator.next(); - if ((isArraySegment(segment) && iterator.hasNext()) || isWildcardSegment(segment)) { - return false; - } - } - return true; - } - - private static boolean isWildcardSegment(String segment) { - return "*".equals(segment); - } - - private static List extractSegments(String path) { - Matcher matcher = BRACKETS_AND_ARRAY_PATTERN.matcher(path); - - int previous = 0; - - List segments = new ArrayList<>(); - while (matcher.find()) { - if (previous != matcher.start()) { - segments.addAll(extractDotSeparatedSegments(path.substring(previous, matcher.start()))); - } - if (matcher.group(1) != null) { - segments.add(matcher.group(1)); - } - else { - segments.add(matcher.group()); - } - previous = matcher.end(0); - } - - if (previous < path.length()) { - segments.addAll(extractDotSeparatedSegments(path.substring(previous))); - } - - return segments; - } - - private static List extractDotSeparatedSegments(String path) { - List segments = new ArrayList<>(); - for (String segment : path.split("\\.")) { - if (segment.length() > 0) { - segments.add(segment); - } - } - return segments; - } - - /** - * The type of a field path. - */ - enum PathType { - - /** - * The path identifies a single item in the payload. - */ - SINGLE, - - /** - * The path identifies multiple items in the payload. - */ - MULTI - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPaths.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPaths.java deleted file mode 100644 index 3c3251c36..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldPaths.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2014-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; - -/** - * {@code JsonFieldPaths} provides support for extracting fields paths from JSON - * structures and identifying uncommon paths. - * - * @author Andy Wilkinson - */ -final class JsonFieldPaths { - - private final Set uncommonFieldPaths; - - private JsonFieldPaths(Set uncommonFieldPaths) { - this.uncommonFieldPaths = uncommonFieldPaths; - } - - Set getUncommon() { - return this.uncommonFieldPaths; - } - - static JsonFieldPaths from(Collection items) { - Set> itemsFieldPaths = new HashSet<>(); - Set allFieldPaths = new HashSet<>(); - for (Object item : items) { - Set paths = new LinkedHashSet<>(); - from(paths, "", item); - itemsFieldPaths.add(paths); - allFieldPaths.addAll(paths); - } - Set uncommonFieldPaths = new HashSet<>(); - for (Set itemFieldPaths : itemsFieldPaths) { - Set uncommonForItem = new HashSet<>(allFieldPaths); - uncommonForItem.removeAll(itemFieldPaths); - uncommonFieldPaths.addAll(uncommonForItem); - } - return new JsonFieldPaths(uncommonFieldPaths); - } - - private static void from(Set paths, String parent, Object object) { - if (object instanceof List) { - String path = append(parent, "[]"); - paths.add(path); - from(paths, path, (List) object); - } - else if (object instanceof Map) { - from(paths, parent, (Map) object); - } - else if (ExtractedField.ABSENT.equals(object)) { - paths.add(parent); - } - } - - private static void from(Set paths, String parent, List items) { - for (Object item : items) { - from(paths, parent, item); - } - } - - private static void from(Set paths, String parent, Map map) { - for (Entry entry : map.entrySet()) { - String path = append(parent, entry.getKey()); - paths.add(path); - from(paths, path, entry.getValue()); - } - } - - private static String append(String path, Object suffix) { - return (path.length() == 0) ? ("" + suffix) : (path + "." + suffix); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldProcessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldProcessor.java deleted file mode 100644 index 0fb7ffa72..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldProcessor.java +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.payload.JsonFieldPath.PathType; - -/** - * A {@code JsonFieldProcessor} processes a payload's fields, allowing them to be - * extracted and removed. - * - * @author Andy Wilkinson - * - */ -final class JsonFieldProcessor { - - boolean hasField(String path, Object payload) { - HasFieldMatchCallback callback = new HasFieldMatchCallback(); - traverse(new ProcessingContext(payload, JsonFieldPath.compile(path)), callback); - return callback.fieldFound(); - } - - ExtractedField extract(String path, Object payload) { - JsonFieldPath compiledPath = JsonFieldPath.compile(path); - final List values = new ArrayList<>(); - traverse(new ProcessingContext(payload, compiledPath), new MatchCallback() { - - @Override - public void foundMatch(Match match) { - values.add(match.getValue()); - } - - @Override - public void absent() { - values.add(ExtractedField.ABSENT); - } - - }); - if (values.isEmpty()) { - values.add(ExtractedField.ABSENT); - } - return new ExtractedField((compiledPath.getType() != PathType.SINGLE) ? values : values.get(0), - compiledPath.getType()); - } - - void remove(String path, Object payload) { - traverse(new ProcessingContext(payload, JsonFieldPath.compile(path)), new MatchCallback() { - - @Override - public void foundMatch(Match match) { - match.remove(); - } - - }); - } - - void removeSubsection(String path, Object payload) { - traverse(new ProcessingContext(payload, JsonFieldPath.compile(path)), new MatchCallback() { - - @Override - public void foundMatch(Match match) { - match.removeSubsection(); - } - - }); - } - - private void traverse(ProcessingContext context, MatchCallback matchCallback) { - String segment = context.getSegment(); - if (JsonFieldPath.isArraySegment(segment)) { - if (context.getPayload() instanceof Collection) { - handleCollectionPayload(context, matchCallback); - } - } - else if (context.getPayload() instanceof Map) { - handleMapPayload(context, matchCallback); - } - } - - private void handleCollectionPayload(ProcessingContext context, MatchCallback matchCallback) { - handleCollectionPayload((Collection) context.getPayload(), matchCallback, context); - } - - private void handleCollectionPayload(Collection collection, MatchCallback matchCallback, - ProcessingContext context) { - if (context.isLeaf()) { - matchCallback.foundMatch(new LeafCollectionMatch(collection, context.getParentMatch())); - } - else { - Iterator items = collection.iterator(); - while (items.hasNext()) { - Object item = items.next(); - traverse(context.descend(item, new CollectionMatch(items, collection, item, context.getParentMatch())), - matchCallback); - } - } - } - - private void handleWildcardPayload(Collection collection, MatchCallback matchCallback, - ProcessingContext context) { - Iterator items = collection.iterator(); - if (context.isLeaf()) { - while (items.hasNext()) { - Object item = items.next(); - matchCallback.foundMatch(new CollectionMatch(items, collection, item, context.getParentMatch())); - } - } - else { - while (items.hasNext()) { - Object item = items.next(); - traverse(context.descend(item, new CollectionMatch(items, collection, item, context.getParentMatch())), - matchCallback); - } - } - } - - private void handleMapPayload(ProcessingContext context, MatchCallback matchCallback) { - Map map = context.getPayload(); - if (map.containsKey(context.getSegment())) { - Object item = map.get(context.getSegment()); - MapMatch mapMatch = new MapMatch(item, map, context.getSegment(), context.getParentMatch()); - if (context.isLeaf()) { - matchCallback.foundMatch(mapMatch); - } - else { - traverse(context.descend(item, mapMatch), matchCallback); - } - } - else if ("*".equals(context.getSegment())) { - handleWildcardPayload(map.values(), matchCallback, context); - } - else { - matchCallback.absent(); - } - } - - /** - * {@link MatchCallback} use to determine whether a payload has a particular field. - */ - private static final class HasFieldMatchCallback implements MatchCallback { - - private MatchType matchType = MatchType.NONE; - - @Override - public void foundMatch(Match match) { - this.matchType = this.matchType - .combinedWith((match.getValue() != null) ? MatchType.NON_NULL : MatchType.NULL); - } - - @Override - public void absent() { - this.matchType = this.matchType.combinedWith(MatchType.ABSENT); - } - - boolean fieldFound() { - return this.matchType == MatchType.NON_NULL || this.matchType == MatchType.NULL; - } - - private enum MatchType { - - ABSENT, MIXED, NONE, NULL, NON_NULL; - - MatchType combinedWith(MatchType matchType) { - if (this == NONE || this == matchType) { - return matchType; - } - return MIXED; - } - - } - - } - - private static final class MapMatch implements Match { - - private final Object item; - - private final Map map; - - private final String segment; - - private final Match parent; - - private MapMatch(Object item, Map map, String segment, Match parent) { - this.item = item; - this.map = map; - this.segment = segment; - this.parent = parent; - } - - @Override - public Object getValue() { - return this.item; - } - - @Override - public void remove() { - Object removalCandidate = this.map.get(this.segment); - if (isMapWithEntries(removalCandidate) || isCollectionWithNonScalarEntries(removalCandidate)) { - return; - } - this.map.remove(this.segment); - if (this.map.isEmpty() && this.parent != null) { - this.parent.remove(); - } - } - - @Override - public void removeSubsection() { - this.map.remove(this.segment); - if (this.map.isEmpty() && this.parent != null) { - this.parent.removeSubsection(); - } - } - - private boolean isMapWithEntries(Object object) { - return object instanceof Map && !((Map) object).isEmpty(); - } - - private boolean isCollectionWithNonScalarEntries(Object object) { - if (!(object instanceof Collection)) { - return false; - } - for (Object entry : (Collection) object) { - if (entry instanceof Map || entry instanceof Collection) { - return true; - } - } - return false; - } - - } - - private static final class CollectionMatch implements Match { - - private final Iterator items; - - private final Collection collection; - - private final Object item; - - private final Match parent; - - private CollectionMatch(Iterator items, Collection collection, Object item, Match parent) { - this.items = items; - this.collection = collection; - this.item = item; - this.parent = parent; - } - - @Override - public Object getValue() { - return this.item; - } - - @Override - public void remove() { - if (!itemIsEmpty()) { - return; - } - this.items.remove(); - if (this.collection.isEmpty() && this.parent != null) { - this.parent.remove(); - } - } - - @Override - public void removeSubsection() { - this.items.remove(); - if (this.collection.isEmpty() && this.parent != null) { - this.parent.removeSubsection(); - } - } - - private boolean itemIsEmpty() { - return !isMapWithEntries(this.item) && !isCollectionWithEntries(this.item); - } - - private boolean isMapWithEntries(Object object) { - return object instanceof Map && !((Map) object).isEmpty(); - } - - private boolean isCollectionWithEntries(Object object) { - return object instanceof Collection && !((Collection) object).isEmpty(); - } - - } - - private static final class LeafCollectionMatch implements Match { - - private final Collection collection; - - private final Match parent; - - private LeafCollectionMatch(Collection collection, Match parent) { - this.collection = collection; - this.parent = parent; - } - - @Override - public Collection getValue() { - return this.collection; - } - - @Override - public void remove() { - if (containsOnlyScalars(this.collection)) { - this.collection.clear(); - if (this.parent != null) { - this.parent.remove(); - } - } - } - - @Override - public void removeSubsection() { - this.collection.clear(); - if (this.parent != null) { - this.parent.removeSubsection(); - } - } - - private boolean containsOnlyScalars(Collection collection) { - for (Object item : collection) { - if (item instanceof Collection || item instanceof Map) { - return false; - } - } - return true; - } - - } - - private interface MatchCallback { - - void foundMatch(Match match); - - default void absent() { - } - - } - - private interface Match { - - Object getValue(); - - void remove(); - - void removeSubsection(); - - } - - private static final class ProcessingContext { - - private final Object payload; - - private final List segments; - - private final Match parent; - - private final JsonFieldPath path; - - private ProcessingContext(Object payload, JsonFieldPath path) { - this(payload, path, null, null); - } - - private ProcessingContext(Object payload, JsonFieldPath path, List segments, Match parent) { - this.payload = payload; - this.path = path; - this.segments = (segments != null) ? segments : path.getSegments(); - this.parent = parent; - } - - private String getSegment() { - return this.segments.get(0); - } - - @SuppressWarnings("unchecked") - private T getPayload() { - return (T) this.payload; - } - - private boolean isLeaf() { - return this.segments.size() == 1; - } - - private Match getParentMatch() { - return this.parent; - } - - private ProcessingContext descend(Object payload, Match match) { - return new ProcessingContext(payload, this.path, this.segments.subList(1, this.segments.size()), match); - } - - } - - /** - * A field that has been extracted from a JSON payload. - */ - static class ExtractedField { - - static final Object ABSENT = new Object(); - - private final Object value; - - private final PathType type; - - ExtractedField(Object value, PathType type) { - this.value = value; - this.type = type; - } - - Object getValue() { - return this.value; - } - - PathType getType() { - return this.type; - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldType.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldType.java deleted file mode 100644 index 54b4bb04f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldType.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Locale; - -import org.springframework.util.StringUtils; - -/** - * An enumeration of the possible types for a field in a JSON request or response payload. - * - * @author Andy Wilkinson - */ -public enum JsonFieldType { - - /** - * An array. - */ - ARRAY, - - /** - * A boolean value. - */ - BOOLEAN, - - /** - * An object (map). - */ - OBJECT, - - /** - * A number. - */ - NUMBER, - - /** - * {@code null}. - */ - NULL, - - /** - * A string. - */ - STRING, - - /** - * A variety of different types. - */ - VARIES; - - @Override - public String toString() { - return StringUtils.capitalize(this.name().toLowerCase(Locale.ENGLISH)); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypes.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypes.java deleted file mode 100644 index 13d7fc3a5..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypes.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * {@link JsonFieldType Types} for a field discovered in a JSON payload. - * - * @author Andy Wilkinson - */ -class JsonFieldTypes implements Iterable { - - private final Set fieldTypes; - - JsonFieldTypes(JsonFieldType fieldType) { - this(Collections.singleton(fieldType)); - } - - JsonFieldTypes(Set fieldTypes) { - this.fieldTypes = fieldTypes; - } - - JsonFieldType coalesce(boolean optional) { - Set types = new HashSet<>(this.fieldTypes); - if (optional && types.size() > 1) { - types.remove(JsonFieldType.NULL); - } - if (types.size() == 1) { - return types.iterator().next(); - } - return JsonFieldType.VARIES; - } - - @Override - public Iterator iterator() { - return this.fieldTypes.iterator(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypesDiscoverer.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypesDiscoverer.java deleted file mode 100644 index 0c683d416..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/JsonFieldTypesDiscoverer.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.payload.JsonFieldPath.PathType; -import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; - -/** - * Discovers the types of the fields found at a path in a JSON request or response - * payload. - * - * @author Andy Wilkinson - */ -class JsonFieldTypesDiscoverer { - - private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor(); - - JsonFieldTypes discoverFieldTypes(String path, Object payload) { - ExtractedField extractedField = this.fieldProcessor.extract(path, payload); - Object value = extractedField.getValue(); - if (value instanceof Collection && extractedField.getType() == PathType.MULTI) { - Collection values = (Collection) value; - if (allAbsent(values)) { - throw new FieldDoesNotExistException(path); - } - Set fieldTypes = new HashSet<>(); - for (Object item : values) { - fieldTypes.add(determineFieldType(item)); - } - return new JsonFieldTypes(fieldTypes); - } - if (value == ExtractedField.ABSENT) { - throw new FieldDoesNotExistException(path); - } - return new JsonFieldTypes(determineFieldType(value)); - } - - private JsonFieldType determineFieldType(Object fieldValue) { - if (fieldValue == null || fieldValue == ExtractedField.ABSENT) { - return JsonFieldType.NULL; - } - if (fieldValue instanceof String) { - return JsonFieldType.STRING; - } - if (fieldValue instanceof Map) { - return JsonFieldType.OBJECT; - } - if (fieldValue instanceof Collection) { - return JsonFieldType.ARRAY; - } - if (fieldValue instanceof Boolean) { - return JsonFieldType.BOOLEAN; - } - return JsonFieldType.NUMBER; - } - - private boolean allAbsent(Collection values) { - for (Object value : values) { - if (value != ExtractedField.ABSENT) { - return false; - } - } - return true; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java deleted file mode 100644 index 4b1c13686..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadDocumentation.java +++ /dev/null @@ -1,1543 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.snippet.Attributes; -import org.springframework.restdocs.snippet.Attributes.Attribute; - -/** - * Static factory methods for documenting a RESTful API's request and response payloads. - * - * @author Andreas Evers - * @author Andy Wilkinson - * @author Marcel Overdijk - */ -public abstract class PayloadDocumentation { - - private PayloadDocumentation() { - - } - - /** - * Creates a {@code FieldDescriptor} that describes a field with the given - * {@code path}. - *

- * When documenting an XML payload, the {@code path} uses XPath, i.e. '/' is used to - * descend to a child node. - *

- * When documenting a JSON payload, the {@code path} uses '.' to descend into a child - * object and ' {@code []}' to descend into an array. For example, with this JSON - * payload: - * - *

-	 * {
-	 *    "a":{
-	 *        "b":[
-	 *            {
-	 *                "c":"one"
-	 *            },
-	 *            {
-	 *                "c":"two"
-	 *            },
-	 *            {
-	 *                "d":"three"
-	 *            }
-	 *        ]
-	 *    }
-	 * }
-	 * 
- * - * The following paths are all present: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Paths that are present and their values
PathValue
{@code a}An object containing "b"
{@code a.b}An array containing three objects
{@code a.b[]}An array containing three objects
{@code a.b[].c}An array containing the strings "one" and "two"
{@code a.b[].d}The string "three"
- * @param path the path of the field - * @return a {@code FieldDescriptor} ready for further configuration - */ - public static FieldDescriptor fieldWithPath(String path) { - return new FieldDescriptor(path); - } - - /** - * Creates a {@code FieldDescriptor} that describes a subsection, i.e. a field and all - * of its descendants, with the given {@code path}. - *

- * When documenting an XML payload, the {@code path} uses XPath, i.e. '/' is used to - * descend to a child node. - *

- * When documenting a JSON payload, the {@code path} uses '.' to descend into a child - * object and ' {@code []}' to descend into an array. For example, with this JSON - * payload: - * - *

-	 * {
-	 *    "a":{
-	 *        "b":[
-	 *            {
-	 *                "c":"one"
-	 *            },
-	 *            {
-	 *                "c":"two"
-	 *            },
-	 *            {
-	 *                "d":"three"
-	 *            }
-	 *        ]
-	 *    }
-	 * }
-	 * 
- * - * The following paths are all present: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Paths that are present and their values
PathValue
{@code a}An object containing "b"
{@code a.b}An array containing three objects
{@code a.b[]}An array containing three objects
{@code a.b[].c}An array containing the strings "one" and "two"
{@code a.b[].d}The string "three"
- *

- * A subsection descriptor for the array with the path {@code a.b[]} will also - * describe its descendants {@code a.b[].c} and {@code a.b[].d}. - * @param path the path of the subsection - * @return a {@code SubsectionDescriptor} ready for further configuration - */ - public static SubsectionDescriptor subsectionWithPath(String path) { - return new SubsectionDescriptor(path); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operations's - * request payload. The fields will be documented using the given {@code descriptors}. - *

- * If a field is present in the request payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see FieldDescriptor#description(Object) - */ - public static RequestFieldsSnippet requestFields(FieldDescriptor... descriptors) { - return requestFields(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operations's - * request payload. The fields will be documented using the given {@code descriptors}. - *

- * If a field is present in the request payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet requestFields(List descriptors) { - return new RequestFieldsSnippet(descriptors); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operations's - * request payload. The fields will be documented using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(FieldDescriptor... descriptors) { - return relaxedRequestFields(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operations's - * request payload. The fields will be documented using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(List descriptors) { - return new RequestFieldsSnippet(descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * request payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is present in the request payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet requestFields(Map attributes, FieldDescriptor... descriptors) { - return requestFields(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * request payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is present in the request payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the request, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet requestFields(Map attributes, - List descriptors) { - return new RequestFieldsSnippet(descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * request payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(Map attributes, - FieldDescriptor... descriptors) { - return relaxedRequestFields(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * request payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(Map attributes, - List descriptors) { - return new RequestFieldsSnippet(descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of API - * operations's request payload extracted by the given {@code subsectionExtractor}. - * The fields will be documented using the given {@code descriptors}. - *

- * If a field is present in the subsection of the request payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur.For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet requestFields(PayloadSubsectionExtractor subsectionExtractor, - FieldDescriptor... descriptors) { - return requestFields(subsectionExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields in the subsection of the - * API operations's request payload extracted by the given {@code subsectionExtractor} - * . The fields will be documented using the given {@code descriptors}. - *

- * If a field is present in the subsection of the request payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet requestFields(PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - return new RequestFieldsSnippet(subsectionExtractor, descriptors); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of the - * API operations's request payload extracted by the given {@code subsectionExtractor} - * . The fields will be documented using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(PayloadSubsectionExtractor subsectionExtractor, - FieldDescriptor... descriptors) { - return relaxedRequestFields(subsectionExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of the - * API operations's request payload extracted by the given {@code subsectionExtractor} - * . The fields will be documented using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - return new RequestFieldsSnippet(subsectionExtractor, descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of the - * API operation's request payload extracted by the given {@code subsectionExtractor}. - * The fields will be documented using the given {@code descriptors} and the given - * {@code attributes} will be available during snippet generation. - *

- * If a field is present in the subsection of the request payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet requestFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, FieldDescriptor... descriptors) { - return requestFields(subsectionExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of the - * API operation's request payload extracted by the given {@code subsectionExtractor}. - * The fields will be documented using the given {@code descriptors} and the given - * {@code attributes} will be available during snippet generation. - *

- * If a field is present in the subsection of the request payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet requestFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, List descriptors) { - return new RequestFieldsSnippet(subsectionExtractor, descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of the - * API operation's request payload extracted by the given {@code subsectionExtractor}. - * The fields will be documented using the given {@code descriptors} and the given - * {@code attributes} will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, FieldDescriptor... descriptors) { - return relaxedRequestFields(subsectionExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the subsection of the - * API operation's request payload extracted by the given {@code subsectionExtractor}. - * The fields will be documented using the given {@code descriptors} and the given - * {@code attributes} will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestFieldsSnippet relaxedRequestFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, List descriptors) { - return new RequestFieldsSnippet(subsectionExtractor, descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors}. - *

- * If a field is present in the payload of the request part, but is not documented by - * one of the descriptors, a failure will occur when the snippet is invoked. - * Similarly, if a field is documented, is not marked as optional, and is not present - * in the request part's payload, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, FieldDescriptor... descriptors) { - return requestPartFields(part, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors}. - *

- * If a field is present in the payload of the request part, but is not documented by - * one of the descriptors, a failure will occur when the snippet is invoked. - * Similarly, if a field is documented, is not marked as optional, and is not present - * in the request part's payload, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, List descriptors) { - return new RequestPartFieldsSnippet(part, descriptors); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, FieldDescriptor... descriptors) { - return relaxedRequestPartFields(part, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, List descriptors) { - return new RequestPartFieldsSnippet(part, descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors} and the given {@code attributes} will be - * available during snippet generation. - *

- * If a field is present in the payload of the request part, but is not documented by - * one of the descriptors, a failure will occur when the snippet is invoked. - * Similarly, if a field is documented, is not marked as optional, and is not present - * in the request part's payload, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, Map attributes, - FieldDescriptor... descriptors) { - return requestPartFields(part, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors} and the given {@code attributes} will be - * available during snippet generation. - *

- * If a field is present in the payload of the request part, but is not documented by - * one of the descriptors, a failure will occur when the snippet is invoked. - * Similarly, if a field is documented, is not marked as optional, and is not present - * in the request part's payload, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, Map attributes, - List descriptors) { - return new RequestPartFieldsSnippet(part, descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors} and the given {@code attributes} will be - * available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, Map attributes, - FieldDescriptor... descriptors) { - return relaxedRequestPartFields(part, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the specified - * {@code part} of the API operations's request payload. The fields will be documented - * using the given {@code descriptors} and the given {@code attributes} will be - * available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, Map attributes, - List descriptors) { - return new RequestPartFieldsSnippet(part, descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors}. - *

- * If a field is present in the subsection of the request part payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the subsection's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, FieldDescriptor... descriptors) { - return requestPartFields(part, subsectionExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors}. - *

- * If a field is present in the subsection of the request part payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the subsection's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, List descriptors) { - return new RequestPartFieldsSnippet(part, subsectionExtractor, descriptors); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, FieldDescriptor... descriptors) { - return relaxedRequestPartFields(part, subsectionExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors}. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, List descriptors) { - return new RequestPartFieldsSnippet(part, subsectionExtractor, descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the givne {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors} and the given {@code attributes} - * will be available during snippet generation. - *

- * If a field is present in the subsection of the request part payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, Map attributes, - FieldDescriptor... descriptors) { - return requestPartFields(part, subsectionExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors} and the given {@code attributes} - * will be available during snippet generation. - *

- * If a field is present in the subsection of the request part payload, but is not - * documented by one of the descriptors, a failure will occur when the snippet is - * invoked. Similarly, if a field is documented, is not marked as optional, and is not - * present in the subsection, a failure will also occur. For payloads with a - * hierarchical structure, documenting a field with a - * {@link #subsectionWithPath(String) subsection descriptor} will mean that all of its - * descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet requestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, Map attributes, - List descriptors) { - return new RequestPartFieldsSnippet(part, subsectionExtractor, descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors} and the given {@code attributes} - * will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, Map attributes, - FieldDescriptor... descriptors) { - return relaxedRequestPartFields(part, subsectionExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the - * specified {@code part} of the API operations's request payload. The subsection will - * be extracted by the given {@code subsectionExtractor}. The fields will be - * documented using the given {@code descriptors} and the given {@code attributes} - * will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param part the part name - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the request part's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static RequestPartFieldsSnippet relaxedRequestPartFields(String part, - PayloadSubsectionExtractor subsectionExtractor, Map attributes, - List descriptors) { - return new RequestPartFieldsSnippet(part, subsectionExtractor, descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * . - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static ResponseFieldsSnippet responseFields(FieldDescriptor... descriptors) { - return responseFields(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * . - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet responseFields(List descriptors) { - return new ResponseFieldsSnippet(descriptors); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * . - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(FieldDescriptor... descriptors) { - return relaxedResponseFields(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * . - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(List descriptors) { - return new ResponseFieldsSnippet(descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static ResponseFieldsSnippet responseFields(Map attributes, FieldDescriptor... descriptors) { - return responseFields(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response, - * a failure will also occur. For payloads with a hierarchical structure, documenting - * a field with a {@link #subsectionWithPath(String) subsection descriptor} will mean - * that all of its descendants are also treated as having been documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static ResponseFieldsSnippet responseFields(Map attributes, - List descriptors) { - return new ResponseFieldsSnippet(descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(Map attributes, - FieldDescriptor... descriptors) { - return relaxedResponseFields(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of the API operation's - * response payload. The fields will be documented using the given {@code descriptors} - * and the given {@code attributes} will be available during snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(Map attributes, - List descriptors) { - return new ResponseFieldsSnippet(descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} . - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response - * payload, a failure will also occur. For payloads with a hierarchical structure, - * documenting a field with a {@link #subsectionWithPath(String) subsection - * descriptor} will mean that all of its descendants are also treated as having been - * documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet responseFields(PayloadSubsectionExtractor subsectionExtractor, - FieldDescriptor... descriptors) { - return responseFields(subsectionExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} . - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response - * payload, a failure will also occur. For payloads with a hierarchical structure, - * documenting a field with a {@link #subsectionWithPath(String) subsection - * descriptor} will mean that all of its descendants are also treated as having been - * documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet responseFields(PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - return new ResponseFieldsSnippet(subsectionExtractor, descriptors); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} . - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(PayloadSubsectionExtractor subsectionExtractor, - FieldDescriptor... descriptors) { - return relaxedResponseFields(subsectionExtractor, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} . - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - return new ResponseFieldsSnippet(subsectionExtractor, descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} and the given {@code attributes} will be available during - * snippet generation. - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response - * payload, a failure will also occur. For payloads with a hierarchical structure, - * documenting a field with a {@link #subsectionWithPath(String) subsection - * descriptor} will mean that all of its descendants are also treated as having been - * documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet responseFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, FieldDescriptor... descriptors) { - return responseFields(subsectionExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} and the given {@code attributes} will be available during - * snippet generation. - *

- * If a field is present in the response payload, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * field is documented, is not marked as optional, and is not present in the response - * payload, a failure will also occur. For payloads with a hierarchical structure, - * documenting a field with a {@link #subsectionWithPath(String) subsection - * descriptor} will mean that all of its descendants are also treated as having been - * documented. - *

- * If you do not want to document a field or subsection, a descriptor can be - * {@link FieldDescriptor#ignored configured to ignore it}. The ignored field or - * subsection will not appear in the generated snippet and the failure described above - * will not occur. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet responseFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, List descriptors) { - return new ResponseFieldsSnippet(subsectionExtractor, descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} and the given {@code attributes} will be available during - * snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, FieldDescriptor... descriptors) { - return relaxedResponseFields(subsectionExtractor, attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the fields of a subsection of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The fields will be documented using the given - * {@code descriptors} and the given {@code attributes} will be available during - * snippet generation. - *

- * If a field is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented fields will be ignored. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @param descriptors the descriptions of the response payload's fields - * @return the snippet that will document the fields - * @since 1.2.0 - * @see #fieldWithPath(String) - * @see #subsectionWithPath(String) - * @see #beneathPath(String) - */ - public static ResponseFieldsSnippet relaxedResponseFields(PayloadSubsectionExtractor subsectionExtractor, - Map attributes, List descriptors) { - return new ResponseFieldsSnippet(subsectionExtractor, descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the body of the API operation's - * request payload. - * @return the snippet that will document the request body - */ - public static RequestBodySnippet requestBody() { - return new RequestBodySnippet(); - } - - /** - * Returns a {@code Snippet} that will document the body of the API operation's - * request payload. The given attributes will be made available during snippet - * generation. - * @param attributes the attributes - * @return the snippet that will document the request body - */ - public static RequestBodySnippet requestBody(Map attributes) { - return new RequestBodySnippet(attributes); - } - - /** - * Returns a {@code Snippet} that will document a subsection of the body of the API - * operation's request payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. - * @param subsectionExtractor the subsection extractor - * @return the snippet that will document the request body subsection - */ - public static RequestBodySnippet requestBody(PayloadSubsectionExtractor subsectionExtractor) { - return new RequestBodySnippet(subsectionExtractor); - } - - /** - * Returns a {@code Snippet} that will document a subsection of the body of the API - * operation's request payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The given attributes will be made available during - * snippet generation. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @return the snippet that will document the request body subsection - */ - public static RequestBodySnippet requestBody(PayloadSubsectionExtractor subsectionExtractor, - Map attributes) { - return new RequestBodySnippet(subsectionExtractor, attributes); - } - - /** - * Returns a {@code Snippet} that will document the body of the API operation's - * response payload. - * @return the snippet that will document the response body - */ - public static ResponseBodySnippet responseBody() { - return new ResponseBodySnippet(); - } - - /** - * Returns a {@code Snippet} that will document the body of the API operation's - * response payload. The given attributes will be made available during snippet - * generation. - * @param attributes the attributes - * @return the snippet that will document the response body - */ - public static ResponseBodySnippet responseBody(Map attributes) { - return new ResponseBodySnippet(attributes); - } - - /** - * Returns a {@code Snippet} that will document a subsection of the body of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. - * @param subsectionExtractor the subsection extractor - * @return the snippet that will document the response body subsection - */ - public static ResponseBodySnippet responseBody(PayloadSubsectionExtractor subsectionExtractor) { - return new ResponseBodySnippet(subsectionExtractor); - } - - /** - * Returns a {@code Snippet} that will document a subsection of the body of the API - * operation's response payload. The subsection will be extracted using the given - * {@code subsectionExtractor}. The given attributes will be made available during - * snippet generation. - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @return the snippet that will document the response body subsection - */ - public static ResponseBodySnippet responseBody(PayloadSubsectionExtractor subsectionExtractor, - Map attributes) { - return new ResponseBodySnippet(subsectionExtractor, attributes); - } - - /** - * Returns a {@code Snippet} that will document the body of specified part of the API - * operation's request payload. - * @param partName the name of the request part - * @return the snippet that will document the response body - */ - public static RequestPartBodySnippet requestPartBody(String partName) { - return new RequestPartBodySnippet(partName); - } - - /** - * Returns a {@code Snippet} that will document the body of specified part of the API - * operation's request payload. The given attributes will be made available during - * snippet generation. - * @param partName the name of the request part - * @param attributes the attributes - * @return the snippet that will document the response body - */ - public static RequestPartBodySnippet requestPartBody(String partName, Map attributes) { - return new RequestPartBodySnippet(partName, attributes); - } - - /** - * Returns a {@code Snippet} that will document a subsection of the body of specified - * part of the API operation's request payload. The subsection will be extracted using - * the given {@code subsectionExtractor}. - * @param partName the name of the request part - * @param subsectionExtractor the subsection extractor - * @return the snippet that will document the response body - */ - public static RequestPartBodySnippet requestPartBody(String partName, - PayloadSubsectionExtractor subsectionExtractor) { - return new RequestPartBodySnippet(partName, subsectionExtractor); - } - - /** - * Returns a {@code Snippet} that will document a subsection of the body of specified - * part of the API operation's request payload. The subsection will be extracted using - * the given {@code subsectionExtractor}. The given attributes will be made available - * during snippet generation. - * @param partName the name of the request part - * @param subsectionExtractor the subsection extractor - * @param attributes the attributes - * @return the snippet that will document the response body - */ - public static RequestPartBodySnippet requestPartBody(String partName, - PayloadSubsectionExtractor subsectionExtractor, Map attributes) { - return new RequestPartBodySnippet(partName, subsectionExtractor, attributes); - } - - /** - * Creates a copy of the given {@code descriptors} with the given {@code pathPrefix} - * applied to their paths. - * @param pathPrefix the path prefix - * @param descriptors the descriptors to copy - * @return the copied descriptors with the prefix applied - */ - public static List applyPathPrefix(String pathPrefix, List descriptors) { - List prefixedDescriptors = new ArrayList<>(); - for (FieldDescriptor descriptor : descriptors) { - String prefixedPath = pathPrefix + descriptor.getPath(); - FieldDescriptor prefixedDescriptor = (descriptor instanceof SubsectionDescriptor) - ? new SubsectionDescriptor(prefixedPath) : new FieldDescriptor(prefixedPath); - prefixedDescriptor.description(descriptor.getDescription()) - .type(descriptor.getType()) - .attributes(asArray(descriptor.getAttributes())); - if (descriptor.isIgnored()) { - prefixedDescriptor.ignored(); - } - if (descriptor.isOptional()) { - prefixedDescriptor.optional(); - } - prefixedDescriptors.add(prefixedDescriptor); - } - return prefixedDescriptors; - } - - /** - * Returns a {@link PayloadSubsectionExtractor} that will extract the subsection of - * the JSON payload found beneath the given {@code path}. - * @param path the path - * @return the subsection extractor - * @since 1.2.0 - */ - public static PayloadSubsectionExtractor beneathPath(String path) { - return new FieldPathPayloadSubsectionExtractor(path); - } - - private static Attribute[] asArray(Map attributeMap) { - List attributes = new ArrayList<>(); - for (Map.Entry attribute : attributeMap.entrySet()) { - attributes.add(Attributes.key(attribute.getKey()).value(attribute.getValue())); - } - return attributes.toArray(new Attribute[attributes.size()]); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadHandlingException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadHandlingException.java deleted file mode 100644 index 6098e01ae..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadHandlingException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -/** - * Thrown to indicate that a failure has occurred during payload handling. - * - * @author Andy Wilkinson - * - */ -@SuppressWarnings("serial") -class PayloadHandlingException extends RuntimeException { - - /** - * Creates a new {@code PayloadHandlingException} with the given {@code message}. - * @param message the message - * @since 1.2.0 - */ - PayloadHandlingException(String message) { - super(message); - } - - /** - * Creates a new {@code PayloadHandlingException} with the given {@code cause}. - * @param cause the cause of the failure - */ - PayloadHandlingException(Throwable cause) { - super(cause); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadSubsectionExtractor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadSubsectionExtractor.java deleted file mode 100644 index 097a8125f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/PayloadSubsectionExtractor.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.List; - -import org.springframework.http.MediaType; - -/** - * Strategy interface for extracting a subsection of a payload. - * - * @param the subsection extractor subclass - * @author Andy Wilkinson - * @since 1.2.0 - */ -public interface PayloadSubsectionExtractor> { - - /** - * Extracts a subsection of the given {@code payload} that has the given - * {@code contentType}. - * @param payload the payload - * @param contentType the content type of the payload - * @return the subsection of the payload - */ - byte[] extractSubsection(byte[] payload, MediaType contentType); - - /** - * Extracts a subsection of the given {@code payload} that has the given - * {@code contentType} and that is described by the given {@code descriptors}. - * @param payload the payload - * @param contentType the content type of the payload - * @param descriptors descriptors that describe the payload - * @return the subsection of the payload - * @since 2.0.4 - */ - default byte[] extractSubsection(byte[] payload, MediaType contentType, List descriptors) { - return extractSubsection(payload, contentType); - } - - /** - * Returns an identifier for the subsection that this extractor will extract. - * @return the identifier - */ - String getSubsectionId(); - - /** - * Returns an extractor with the given {@code subsectionId}. - * @param subsectionId the subsection ID - * @return the customized extractor - */ - T withSubsectionId(String subsectionId); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestBodySnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestBodySnippet.java deleted file mode 100644 index bdbb4b401..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestBodySnippet.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - -/** - * A {@link Snippet} that documents the body of a request. - * - * @author Andy Wilkinson - */ -public class RequestBodySnippet extends AbstractBodySnippet { - - /** - * Creates a new {@code RequestBodySnippet}. - */ - public RequestBodySnippet() { - this(null, null); - } - - /** - * Creates a new {@code RequestBodySnippet} that will document the subsection of the - * request body extracted by the given {@code subsectionExtractor}. - * @param subsectionExtractor the subsection extractor - */ - public RequestBodySnippet(PayloadSubsectionExtractor subsectionExtractor) { - this(subsectionExtractor, null); - } - - /** - * Creates a new {@code RequestBodySnippet} with the given additional - * {@code attributes} that will be included in the model during template rendering. - * @param attributes the additional attributes - */ - public RequestBodySnippet(Map attributes) { - this(null, attributes); - } - - /** - * Creates a new {@code RequestBodySnippet} that will document the subsection of the - * request body extracted by the given {@code subsectionExtractor}. The given - * additional {@code attributes} that will be included in the model during template - * rendering. - * @param subsectionExtractor the subsection extractor - * @param attributes the additional attributes - */ - public RequestBodySnippet(PayloadSubsectionExtractor subsectionExtractor, Map attributes) { - super("request", subsectionExtractor, attributes); - } - - @Override - protected byte[] getContent(Operation operation) throws IOException { - return operation.getRequest().getContent(); - } - - @Override - protected MediaType getContentType(Operation operation) { - return operation.getRequest().getHeaders().getContentType(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java deleted file mode 100644 index 63a39272d..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestFieldsSnippet.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - -/** - * A {@link Snippet} that documents the fields in a request. - * - * @author Andy Wilkinson - * @see PayloadDocumentation#requestFields(FieldDescriptor...) - * @see PayloadDocumentation#requestFields(Map, FieldDescriptor...) - */ -public class RequestFieldsSnippet extends AbstractFieldsSnippet { - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * request using the given {@code descriptors}. Undocumented fields will trigger a - * failure. - * @param descriptors the descriptors - */ - protected RequestFieldsSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * request using the given {@code descriptors}. If {@code ignoreUndocumentedFields} is - * {@code true}, undocumented fields will be ignored and will not trigger a failure. - * @param descriptors the descriptors - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected RequestFieldsSnippet(List descriptors, boolean ignoreUndocumentedFields) { - this(descriptors, null, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. Undocumented fields will trigger a - * failure. - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected RequestFieldsSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected RequestFieldsSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedFields) { - this(null, descriptors, attributes, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * subsection of the request extracted by the given {@code subsectionExtractor} using - * the given {@code descriptors}. Undocumented fields will trigger a failure. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @since 1.2.0 - */ - protected RequestFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - this(subsectionExtractor, descriptors, null, false); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * subsection of the request extracted by the given {@code subsectionExtractor} using - * the given {@code descriptors}. If {@code ignoreUndocumentedFields} is {@code true}, - * undocumented fields will be ignored and will not trigger a failure. - * @param subsectionExtractor the subsection extractor document - * @param descriptors the descriptors - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - * @since 1.2.0 - */ - protected RequestFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, List descriptors, - boolean ignoreUndocumentedFields) { - this(subsectionExtractor, descriptors, null, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * subsection of the request extracted by the given {@code subsectionExtractor} using - * the given {@code descriptors}. The given {@code attributes} will be included in the - * model during template rendering. Undocumented fields will trigger a failure. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param attributes the additional attributes - * @since 1.2.0 - */ - protected RequestFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, List descriptors, - Map attributes) { - this(subsectionExtractor, descriptors, attributes, false); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * subsection of the request extracted by the given {@code subsectionExtractor} using - * the given {@code descriptors}. The given {@code attributes} will be included in the - * model during template rendering. If {@code ignoreUndocumentedFields} is - * {@code true}, undocumented fields will be ignored and will not trigger a failure. - * @param subsectionExtractor the path identifying the subsection of the payload to - * document - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - * @since 1.2.0 - */ - protected RequestFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, List descriptors, - Map attributes, boolean ignoreUndocumentedFields) { - super("request", descriptors, attributes, ignoreUndocumentedFields, subsectionExtractor); - } - - @Override - protected MediaType getContentType(Operation operation) { - return operation.getRequest().getHeaders().getContentType(); - } - - @Override - protected byte[] getContent(Operation operation) throws IOException { - return operation.getRequest().getContent(); - } - - /** - * Returns a new {@code RequestFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestFieldsSnippet and(FieldDescriptor... additionalDescriptors) { - return andWithPrefix("", additionalDescriptors); - } - - /** - * Returns a new {@code RequestFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestFieldsSnippet and(List additionalDescriptors) { - return andWithPrefix("", additionalDescriptors); - } - - /** - * Returns a new {@code RequestFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path - * of each additional descriptor. - * @param pathPrefix the prefix to apply to the additional descriptors - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestFieldsSnippet andWithPrefix(String pathPrefix, FieldDescriptor... additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(); - combinedDescriptors.addAll(getFieldDescriptors()); - combinedDescriptors - .addAll(PayloadDocumentation.applyPathPrefix(pathPrefix, Arrays.asList(additionalDescriptors))); - return new RequestFieldsSnippet(getSubsectionExtractor(), combinedDescriptors, getAttributes(), - isIgnoredUndocumentedFields()); - } - - /** - * Returns a new {@code RequestFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path - * of each additional descriptor. - * @param pathPrefix the prefix to apply to the additional descriptors - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestFieldsSnippet andWithPrefix(String pathPrefix, List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(getFieldDescriptors()); - combinedDescriptors.addAll(PayloadDocumentation.applyPathPrefix(pathPrefix, additionalDescriptors)); - return new RequestFieldsSnippet(getSubsectionExtractor(), combinedDescriptors, getAttributes(), - isIgnoredUndocumentedFields()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestPartBodySnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestPartBodySnippet.java deleted file mode 100644 index 3ab046015..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestPartBodySnippet.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; - -/** - * A {@link Snippet} that documents the body of a request part. - * - * @author Andy Wilkinson - */ -public class RequestPartBodySnippet extends AbstractBodySnippet { - - private final String partName; - - /** - * Creates a new {@code RequestPartBodySnippet} that will document the body of the - * request part with the given {@code partName}. - * @param partName the name of the request part - */ - public RequestPartBodySnippet(String partName) { - this(partName, null, null); - } - - /** - * Creates a new {@code RequestPartBodySnippet} that will document the subsection of - * the body of the request part with the given {@code partName} extracted by the given - * {@code subsectionExtractor}. - * @param partName the name of the request part - * @param subsectionExtractor the subsection extractor - */ - public RequestPartBodySnippet(String partName, PayloadSubsectionExtractor subsectionExtractor) { - this(partName, subsectionExtractor, null); - } - - /** - * Creates a new {@code RequestPartBodySnippet} that will document the body of the - * request part with the given {@code partName}. The given additional - * {@code attributes} will be included in the model during template rendering. - * @param partName the name of the request part - * @param attributes the additional attributes - */ - public RequestPartBodySnippet(String partName, Map attributes) { - this(partName, null, attributes); - } - - /** - * Creates a new {@code RequestPartBodySnippet} that will document the body of the - * request part with the given {@code partName}. The subsection of the body extracted - * by the given {@code subsectionExtractor} will be documented and the given - * additional {@code attributes} that will be included in the model during template - * rendering. - * @param partName the name of the request part - * @param subsectionExtractor the subsection extractor - * @param attributes the additional attributes - */ - public RequestPartBodySnippet(String partName, PayloadSubsectionExtractor subsectionExtractor, - Map attributes) { - super("request-part-" + partName, "request-part", subsectionExtractor, attributes); - this.partName = partName; - } - - @Override - protected byte[] getContent(Operation operation) throws IOException { - return findPart(operation).getContent(); - } - - @Override - protected MediaType getContentType(Operation operation) { - return findPart(operation).getHeaders().getContentType(); - } - - private OperationRequestPart findPart(Operation operation) { - for (OperationRequestPart candidate : operation.getRequest().getParts()) { - if (candidate.getName().equals(this.partName)) { - return candidate; - } - } - throw new SnippetException("A request part named '" + this.partName + "' was not found in the request"); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestPartFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestPartFieldsSnippet.java deleted file mode 100644 index eb7d9aa57..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/RequestPartFieldsSnippet.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; - -/** - * A {@link Snippet} that documents the fields in a request part. - * - * @author Mathieu Pousse - * @author Andy Wilkinson - * @since 1.2.0 - * @see PayloadDocumentation#requestPartFields(String, FieldDescriptor...) - * @see PayloadDocumentation#requestPartFields(String, List) - */ -public class RequestPartFieldsSnippet extends AbstractFieldsSnippet { - - private final String partName; - - /** - * Creates a new {@code RequestPartFieldsSnippet} that will document the fields in the - * request part using the given {@code descriptors}. Undocumented fields will trigger - * a failure. - * @param partName the part name - * @param descriptors the descriptors - */ - protected RequestPartFieldsSnippet(String partName, List descriptors) { - this(partName, descriptors, null, false); - } - - /** - * Creates a new {@code RequestPartFieldsSnippet} that will document the fields in the - * request part using the given {@code descriptors}. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param partName the part name - * @param descriptors the descriptors - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected RequestPartFieldsSnippet(String partName, List descriptors, - boolean ignoreUndocumentedFields) { - this(partName, descriptors, null, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. Undocumented fields will trigger a - * failure. - * @param partName the part name - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected RequestPartFieldsSnippet(String partName, List descriptors, - Map attributes) { - this(partName, descriptors, attributes, false); - } - - /** - * Creates a new {@code RequestFieldsSnippet} that will document the fields in the - * request using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param partName the part name - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected RequestPartFieldsSnippet(String partName, List descriptors, - Map attributes, boolean ignoreUndocumentedFields) { - this(partName, null, descriptors, attributes, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code RequestPartFieldsSnippet} that will document the fields in a - * subsection of the request part using the given {@code descriptors}. The subsection - * will be extracted using the given {@code subsectionExtractor}. Undocumented fields - * will trigger a failure. - * @param partName the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - */ - protected RequestPartFieldsSnippet(String partName, PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - this(partName, subsectionExtractor, descriptors, null, false); - } - - /** - * Creates a new {@code RequestPartFieldsSnippet} that will document the fields in a - * subsection the request part using the given {@code descriptors}. The subsection - * will be extracted using the given {@code subsectionExtractor}. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param partName the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected RequestPartFieldsSnippet(String partName, PayloadSubsectionExtractor subsectionExtractor, - List descriptors, boolean ignoreUndocumentedFields) { - this(partName, subsectionExtractor, descriptors, null, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code RequestPartFieldsSnippet} that will document the fields in a - * subsection of the request part using the given {@code descriptors}. The subsection - * will be extracted using the given {@code subsectionExtractor}. The given - * {@code attributes} will be included in the model during template rendering. - * Undocumented fields will trigger a failure. - * @param partName the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected RequestPartFieldsSnippet(String partName, PayloadSubsectionExtractor subsectionExtractor, - List descriptors, Map attributes) { - this(partName, subsectionExtractor, descriptors, attributes, false); - } - - /** - * Creates a new {@code RequestPartFieldsSnippet} that will document the fields in a - * subsection of the request part using the given {@code descriptors}. The subsection - * will be extracted using the given {@code subsectionExtractor}. The given - * {@code attributes} will be included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param partName the part name - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected RequestPartFieldsSnippet(String partName, PayloadSubsectionExtractor subsectionExtractor, - List descriptors, Map attributes, boolean ignoreUndocumentedFields) { - super("request-part-" + partName, "request-part", descriptors, attributes, ignoreUndocumentedFields, - subsectionExtractor); - this.partName = partName; - } - - @Override - protected MediaType getContentType(Operation operation) { - return findPart(operation).getHeaders().getContentType(); - } - - @Override - protected byte[] getContent(Operation operation) throws IOException { - return findPart(operation).getContent(); - } - - private OperationRequestPart findPart(Operation operation) { - for (OperationRequestPart candidate : operation.getRequest().getParts()) { - if (candidate.getName().equals(this.partName)) { - return candidate; - } - } - throw new SnippetException("A request part named '" + this.partName + "' was not found in the request"); - } - - /** - * Returns a new {@code RequestPartFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestPartFieldsSnippet and(FieldDescriptor... additionalDescriptors) { - return andWithPrefix("", additionalDescriptors); - } - - /** - * Returns a new {@code RequestPartFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestPartFieldsSnippet and(List additionalDescriptors) { - return andWithPrefix("", additionalDescriptors); - } - - /** - * Returns a new {@code RequestFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path - * of each additional descriptor. - * @param pathPrefix the prefix to apply to the additional descriptors - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestPartFieldsSnippet andWithPrefix(String pathPrefix, FieldDescriptor... additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(); - combinedDescriptors.addAll(getFieldDescriptors()); - combinedDescriptors - .addAll(PayloadDocumentation.applyPathPrefix(pathPrefix, Arrays.asList(additionalDescriptors))); - return new RequestPartFieldsSnippet(this.partName, combinedDescriptors, this.getAttributes()); - } - - /** - * Returns a new {@code RequestFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path - * of each additional descriptor. - * @param pathPrefix the prefix to apply to the additional descriptors - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestPartFieldsSnippet andWithPrefix(String pathPrefix, - List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(getFieldDescriptors()); - combinedDescriptors.addAll(PayloadDocumentation.applyPathPrefix(pathPrefix, additionalDescriptors)); - return new RequestPartFieldsSnippet(this.partName, combinedDescriptors, this.getAttributes()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseBodySnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseBodySnippet.java deleted file mode 100644 index c316b54fa..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseBodySnippet.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - -/** - * A {@link Snippet} that documents the body of a response. - * - * @author Andy Wilkinson - */ -public class ResponseBodySnippet extends AbstractBodySnippet { - - /** - * Creates a new {@code ResponseBodySnippet}. - */ - public ResponseBodySnippet() { - this(null, null); - } - - /** - * Creates a new {@code ResponseBodySnippet} that will document the subsection of the - * response body extracted by the given {@code subsectionExtractor}. - * @param subsectionExtractor the subsection extractor - */ - public ResponseBodySnippet(PayloadSubsectionExtractor subsectionExtractor) { - this(subsectionExtractor, null); - } - - /** - * Creates a new {@code ResponseBodySnippet} with the given additional - * {@code attributes} that will be included in the model during template rendering. - * @param attributes the additional attributes - */ - public ResponseBodySnippet(Map attributes) { - this(null, attributes); - } - - /** - * Creates a new {@code ResponseBodySnippet} that will document the subsection of the - * response body extracted by the given {@code subsectionExtractor}. The given - * additional {@code attributes} that will be included in the model during template - * rendering. - * @param subsectionExtractor the subsection extractor - * @param attributes the additional attributes - */ - public ResponseBodySnippet(PayloadSubsectionExtractor subsectionExtractor, Map attributes) { - super("response", subsectionExtractor, attributes); - } - - @Override - protected byte[] getContent(Operation operation) throws IOException { - return operation.getResponse().getContent(); - } - - @Override - protected MediaType getContentType(Operation operation) { - return operation.getResponse().getHeaders().getContentType(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java deleted file mode 100644 index 5e452b7e2..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/ResponseFieldsSnippet.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - -/** - * A {@link Snippet} that documents the fields in a response. - * - * @author Andy Wilkinson - * @see PayloadDocumentation#responseFields(FieldDescriptor...) - * @see PayloadDocumentation#responseFields(Map, FieldDescriptor...) - */ -public class ResponseFieldsSnippet extends AbstractFieldsSnippet { - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the - * response using the given {@code descriptors}. Undocumented fields will trigger a - * failure. - * @param descriptors the descriptors - */ - protected ResponseFieldsSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the - * response using the given {@code descriptors}. If {@code ignoreUndocumentedFields} - * is {@code true}, undocumented fields will be ignored and will not trigger a - * failure. - * @param descriptors the descriptors - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected ResponseFieldsSnippet(List descriptors, boolean ignoreUndocumentedFields) { - this(descriptors, null, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the - * response using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. Undocumented fields will trigger a - * failure. - * @param descriptors the descriptors - * @param attributes the additional attributes - */ - protected ResponseFieldsSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the - * response using the given {@code descriptors}. The given {@code attributes} will be - * included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - */ - protected ResponseFieldsSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedFields) { - this(null, descriptors, attributes, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in a - * subsection of the response using the given {@code descriptors}. The subsection will - * be extracted using the given {@code subsectionExtractor}. Undocumented fields will - * trigger a failure. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @since 1.2.0 - */ - protected ResponseFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, - List descriptors) { - this(subsectionExtractor, descriptors, null, false); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in the - * subsection of the response using the given {@code descriptors}. The subsection will - * be extracted using the given {@code subsectionExtractor}. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - * @since 1.2.0 - */ - protected ResponseFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, - List descriptors, boolean ignoreUndocumentedFields) { - this(subsectionExtractor, descriptors, null, ignoreUndocumentedFields); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in a - * subsection of the response using the given {@code descriptors}. The subsection will - * be extracted using the given {@code subsectionExtractor}. The given - * {@code attributes} will be included in the model during template rendering. - * Undocumented fields will trigger a failure. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param attributes the additional attributes - * @since 1.2.0 - */ - protected ResponseFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, - List descriptors, Map attributes) { - this(subsectionExtractor, descriptors, attributes, false); - } - - /** - * Creates a new {@code ResponseFieldsSnippet} that will document the fields in a - * subsection of the response using the given {@code descriptors}. The subsection will - * be extracted using the given {@code subsectionExtractor}. The given - * {@code attributes} will be included in the model during template rendering. If - * {@code ignoreUndocumentedFields} is {@code true}, undocumented fields will be - * ignored and will not trigger a failure. - * @param subsectionExtractor the subsection extractor - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedFields whether undocumented fields should be ignored - * @since 1.2.0 - */ - protected ResponseFieldsSnippet(PayloadSubsectionExtractor subsectionExtractor, - List descriptors, Map attributes, boolean ignoreUndocumentedFields) { - super("response", descriptors, attributes, ignoreUndocumentedFields, subsectionExtractor); - } - - @Override - protected MediaType getContentType(Operation operation) { - return operation.getResponse().getHeaders().getContentType(); - } - - @Override - protected byte[] getContent(Operation operation) throws IOException { - return operation.getResponse().getContent(); - } - - /** - * Returns a new {@code ResponseFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseFieldsSnippet and(FieldDescriptor... additionalDescriptors) { - return andWithPrefix("", additionalDescriptors); - } - - /** - * Returns a new {@code ResponseFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseFieldsSnippet and(List additionalDescriptors) { - return andWithPrefix("", additionalDescriptors); - } - - /** - * Returns a new {@code ResponseFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path - * of each additional descriptor. - * @param pathPrefix the prefix to apply to the additional descriptors - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseFieldsSnippet andWithPrefix(String pathPrefix, FieldDescriptor... additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(); - combinedDescriptors.addAll(getFieldDescriptors()); - combinedDescriptors - .addAll(PayloadDocumentation.applyPathPrefix(pathPrefix, Arrays.asList(additionalDescriptors))); - return new ResponseFieldsSnippet(getSubsectionExtractor(), combinedDescriptors, this.getAttributes(), - isIgnoredUndocumentedFields()); - } - - /** - * Returns a new {@code ResponseFieldsSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. The given {@code pathPrefix} is applied to the path - * of each additional descriptor. - * @param pathPrefix the prefix to apply to the additional descriptors - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final ResponseFieldsSnippet andWithPrefix(String pathPrefix, List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(getFieldDescriptors()); - combinedDescriptors.addAll(PayloadDocumentation.applyPathPrefix(pathPrefix, additionalDescriptors)); - return new ResponseFieldsSnippet(getSubsectionExtractor(), combinedDescriptors, this.getAttributes(), - isIgnoredUndocumentedFields()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/SubsectionDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/SubsectionDescriptor.java deleted file mode 100644 index 5dc16c521..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/SubsectionDescriptor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -/** - * A description of a subsection, i.e. a field and all of its descendants, in a request or - * response payload. - * - * @author Andy Wilkinson - * @since 1.2.0 - */ -public class SubsectionDescriptor extends FieldDescriptor { - - /** - * Creates a new {@code SubsectionDescriptor} describing the subsection with the given - * {@code path}. - * @param path the path - */ - protected SubsectionDescriptor(String path) { - super(path); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java deleted file mode 100644 index 8ca819900..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/XmlContentHandler.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.ByteArrayInputStream; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; - -/** - * A {@link ContentHandler} for XML content. - * - * @author Andy Wilkinson - */ -class XmlContentHandler implements ContentHandler { - - private final DocumentBuilder documentBuilder; - - private final byte[] rawContent; - - private final List fieldDescriptors; - - XmlContentHandler(byte[] rawContent, List fieldDescriptors) { - try { - this.documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - } - catch (ParserConfigurationException ex) { - throw new IllegalStateException("Failed to create document builder", ex); - } - this.rawContent = rawContent; - this.fieldDescriptors = fieldDescriptors; - readPayload(); - } - - @Override - public List findMissingFields() { - List missingFields = new ArrayList<>(); - Document payload = readPayload(); - for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) { - if (!fieldDescriptor.isOptional()) { - NodeList matchingNodes = findMatchingNodes(fieldDescriptor, payload); - if (matchingNodes.getLength() == 0) { - missingFields.add(fieldDescriptor); - } - } - } - - return missingFields; - } - - private NodeList findMatchingNodes(FieldDescriptor fieldDescriptor, Document payload) { - try { - return (NodeList) createXPath(fieldDescriptor.getPath()).evaluate(payload, XPathConstants.NODESET); - } - catch (XPathExpressionException ex) { - throw new PayloadHandlingException(ex); - } - } - - private Document readPayload() { - try { - return this.documentBuilder.parse(new InputSource(new ByteArrayInputStream(this.rawContent))); - } - catch (Exception ex) { - throw new PayloadHandlingException(ex); - } - } - - private XPathExpression createXPath(String fieldPath) throws XPathExpressionException { - return XPathFactory.newInstance().newXPath().compile(fieldPath); - } - - @Override - public String getUndocumentedContent() { - Document payload = readPayload(); - List matchedButNotRemoved = new ArrayList<>(); - for (FieldDescriptor fieldDescriptor : this.fieldDescriptors) { - NodeList matchingNodes; - try { - matchingNodes = (NodeList) createXPath(fieldDescriptor.getPath()).evaluate(payload, - XPathConstants.NODESET); - } - catch (XPathExpressionException ex) { - throw new PayloadHandlingException(ex); - } - for (int i = 0; i < matchingNodes.getLength(); i++) { - Node node = matchingNodes.item(i); - if (node.getNodeType() == Node.ATTRIBUTE_NODE) { - Attr attr = (Attr) node; - attr.getOwnerElement().removeAttributeNode(attr); - } - else { - if (fieldDescriptor instanceof SubsectionDescriptor || isLeafNode(node)) { - node.getParentNode().removeChild(node); - } - else { - matchedButNotRemoved.add(node); - } - } - - } - } - removeLeafNodes(matchedButNotRemoved); - if (payload.getChildNodes().getLength() > 0) { - return prettyPrint(payload); - } - return null; - } - - private void removeLeafNodes(List candidates) { - boolean changed = true; - while (changed) { - changed = false; - Iterator iterator = candidates.iterator(); - while (iterator.hasNext()) { - Node node = iterator.next(); - if (isLeafNode(node)) { - node.getParentNode().removeChild(node); - iterator.remove(); - changed = true; - } - } - } - } - - private boolean isLeafNode(Node node) { - NodeList childNodes = node.getChildNodes(); - for (int i = 0; i < childNodes.getLength(); i++) { - if (childNodes.item(i) instanceof Element) { - return false; - } - } - return true; - } - - private String prettyPrint(Document document) { - try { - StringWriter stringWriter = new StringWriter(); - StreamResult xmlOutput = new StreamResult(stringWriter); - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setAttribute("indent-number", 4); - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.transform(new DOMSource(document), xmlOutput); - return xmlOutput.getWriter().toString(); - } - catch (Exception ex) { - throw new PayloadHandlingException(ex); - } - } - - @Override - public Object resolveFieldType(FieldDescriptor fieldDescriptor) { - if (fieldDescriptor.getType() != null) { - return fieldDescriptor.getType(); - } - else { - throw new FieldTypeRequiredException("The type of a field in an XML payload " - + "cannot be determined automatically. Please provide a type using " - + "FieldDescriptor.type(Object type)"); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/package-info.java deleted file mode 100644 index 395fb7030..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/payload/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting the payload of a RESTful API's requests and responses. - */ -package org.springframework.restdocs.payload; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java deleted file mode 100644 index cce7a7d1a..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/AbstractParametersSnippet.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; - -/** - * Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that - * document parameters from a request sent to a RESTful resource. - * - * @author Andreas Evers - * @author Andy Wilkinson - */ -public abstract class AbstractParametersSnippet extends TemplatedSnippet { - - private final Map descriptorsByName = new LinkedHashMap<>(); - - private final boolean ignoreUndocumentedParameters; - - /** - * Creates a new {@code AbstractParametersSnippet} that will produce a snippet with - * the given {@code snippetName} that will document parameters using the given - * {@code descriptors}. The given {@code attributes} will be included in the model - * during template rendering. If {@code ignoreUndocumentedParameters} is {@code true}, - * undocumented parameters will be ignored and will not trigger a failure. - * @param snippetName the snippet name - * @param descriptors the descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected AbstractParametersSnippet(String snippetName, List descriptors, - Map attributes, boolean ignoreUndocumentedParameters) { - super(snippetName, attributes); - for (ParameterDescriptor descriptor : descriptors) { - Assert.notNull(descriptor.getName(), "Parameter descriptors must have a name"); - if (!descriptor.isIgnored()) { - Assert.notNull(descriptor.getDescription(), "The descriptor for parameter '" + descriptor.getName() - + "' must either have a description or be marked as " + "ignored"); - } - this.descriptorsByName.put(descriptor.getName(), descriptor); - } - this.ignoreUndocumentedParameters = ignoreUndocumentedParameters; - } - - @Override - protected Map createModel(Operation operation) { - verifyParameterDescriptors(operation); - - Map model = new HashMap<>(); - List> parameters = new ArrayList<>(); - for (Entry entry : this.descriptorsByName.entrySet()) { - ParameterDescriptor descriptor = entry.getValue(); - if (!descriptor.isIgnored()) { - parameters.add(createModelForDescriptor(descriptor)); - } - } - model.put("parameters", parameters); - return model; - } - - private void verifyParameterDescriptors(Operation operation) { - Set actualParameters = extractActualParameters(operation); - Set expectedParameters = new HashSet<>(); - for (Entry entry : this.descriptorsByName.entrySet()) { - if (!entry.getValue().isOptional()) { - expectedParameters.add(entry.getKey()); - } - } - Set undocumentedParameters; - if (this.ignoreUndocumentedParameters) { - undocumentedParameters = Collections.emptySet(); - } - else { - undocumentedParameters = new HashSet<>(actualParameters); - undocumentedParameters.removeAll(this.descriptorsByName.keySet()); - } - Set missingParameters = new HashSet<>(expectedParameters); - missingParameters.removeAll(actualParameters); - - if (!undocumentedParameters.isEmpty() || !missingParameters.isEmpty()) { - verificationFailed(undocumentedParameters, missingParameters); - } - } - - /** - * Extracts the names of the parameters that were present in the given - * {@code operation}. - * @param operation the operation - * @return the parameters - */ - protected abstract Set extractActualParameters(Operation operation); - - /** - * Called when the documented parameters do not match the actual parameters. - * @param undocumentedParameters the parameters that were found in the operation but - * were not documented - * @param missingParameters the parameters that were documented but were not found in - * the operation - */ - protected abstract void verificationFailed(Set undocumentedParameters, Set missingParameters); - - /** - * Returns a {@code Map} of {@link ParameterDescriptor ParameterDescriptors} that will - * be used to generate the documentation key by their - * {@link ParameterDescriptor#getName()}. - * @return the map of path descriptors - */ - protected final Map getParameterDescriptors() { - return this.descriptorsByName; - } - - /** - * Returns whether to ignore undocumented parameters. - * @return {@code true} if undocumented parameters should be ignored, otherwise - * {@code false} - * @since 2.0.5 - */ - protected final boolean isIgnoreUndocumentedParameters() { - return this.ignoreUndocumentedParameters; - } - - /** - * Returns a model for the given {@code descriptor}. - * @param descriptor the descriptor - * @return the model - */ - protected Map createModelForDescriptor(ParameterDescriptor descriptor) { - Map model = new HashMap<>(); - model.put("name", descriptor.getName()); - model.put("description", descriptor.getDescription()); - model.put("optional", descriptor.isOptional()); - model.putAll(descriptor.getAttributes()); - return model; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/FormParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/FormParametersSnippet.java deleted file mode 100644 index 8d5401d51..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/FormParametersSnippet.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.operation.FormParameters; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; - -/** - * A {@link Snippet} that documents the form parameters supported by a RESTful resource. - * - * @author Andy Wilkinson - * @since 3.0.0 - * @see RequestDocumentation#formParameters(ParameterDescriptor...) - * @see RequestDocumentation#formParameters(Map, ParameterDescriptor...) - */ -public class FormParametersSnippet extends AbstractParametersSnippet { - - /** - * Creates a new {@code FormParametersSnippet} that will document the request's form - * parameters using the given {@code descriptors}. Undocumented parameters will - * trigger a failure. - * @param descriptors the parameter descriptors - */ - protected FormParametersSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code FormParametersSnippet} that will document the request's form - * parameters using the given {@code descriptors}. If - * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will - * be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected FormParametersSnippet(List descriptors, boolean ignoreUndocumentedParameters) { - this(descriptors, null, ignoreUndocumentedParameters); - } - - /** - * Creates a new {@code FormParametersSnippet} that will document the request's form - * parameters using the given {@code descriptors}. The given {@code attributes} will - * be included in the model during template rendering. Undocumented parameters will - * trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - */ - protected FormParametersSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code FormParametersSnippet} that will document the request's form - * parameters using the given {@code descriptors}. The given {@code attributes} will - * be included in the model during template rendering. If - * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will - * be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected FormParametersSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedParameters) { - super("form-parameters", descriptors, attributes, ignoreUndocumentedParameters); - } - - @Override - protected void verificationFailed(Set undocumentedParameters, Set missingParameters) { - String message = ""; - if (!undocumentedParameters.isEmpty()) { - message += "Form parameters with the following names were not documented: " + undocumentedParameters; - } - if (!missingParameters.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Form parameters with the following names were not found in the request: " + missingParameters; - } - throw new SnippetException(message); - } - - @Override - protected Set extractActualParameters(Operation operation) { - return FormParameters.from(operation.getRequest()).keySet(); - } - - /** - * Returns a new {@code FormParametersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public FormParametersSnippet and(ParameterDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code FormParametersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public FormParametersSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(getParameterDescriptors().values()); - combinedDescriptors.addAll(additionalDescriptors); - return new FormParametersSnippet(combinedDescriptors, this.getAttributes(), - this.isIgnoreUndocumentedParameters()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java deleted file mode 100644 index a3555211b..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import org.springframework.restdocs.snippet.IgnorableDescriptor; - -/** - * A descriptor of a request or path parameter. - * - * @author Andy Wilkinson - * @see RequestDocumentation#parameterWithName - * - */ -public class ParameterDescriptor extends IgnorableDescriptor { - - private final String name; - - private boolean optional; - - /** - * Creates a new {@code ParameterDescriptor} describing the parameter with the given - * {@code name}. - * @param name the name of the parameter - */ - protected ParameterDescriptor(String name) { - this.name = name; - } - - /** - * Marks the parameter as optional. - * @return {@code this} - */ - public final ParameterDescriptor optional() { - this.optional = true; - return this; - } - - /** - * Returns the name of the parameter being described by this descriptor. - * @return the name of the parameter - */ - public final String getName() { - return this.name; - } - - /** - * Returns {@code true} if the described parameter is optional, otherwise - * {@code false}. - * @return {@code true} if the described parameter is optional, otherwise - * {@code false} - */ - public final boolean isOptional() { - return this.optional; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java deleted file mode 100644 index 3bc84e983..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/PathParametersSnippet.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.util.Assert; - -/** - * A {@link Snippet} that documents the path parameters supported by a RESTful resource. - * - * @author Andy Wilkinson - * @see RequestDocumentation#pathParameters(ParameterDescriptor...) - * @see RequestDocumentation#pathParameters(Map, ParameterDescriptor...) - */ -public class PathParametersSnippet extends AbstractParametersSnippet { - - private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}"); - - /** - * Creates a new {@code PathParametersSnippet} that will document the request's path - * parameters using the given {@code descriptors}. Undocumented parameters will - * trigger a failure. - * @param descriptors the parameter descriptors - */ - protected PathParametersSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code PathParametersSnippet} that will document the request's path - * parameters using the given {@code descriptors}. If - * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will - * be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected PathParametersSnippet(List descriptors, boolean ignoreUndocumentedParameters) { - this(descriptors, null, ignoreUndocumentedParameters); - } - - /** - * Creates a new {@code PathParametersSnippet} that will document the request's path - * parameters using the given {@code descriptors}. The given {@code attributes} will - * be included in the model during template rendering. Undocumented parameters will - * trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - */ - protected PathParametersSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code PathParametersSnippet} that will document the request's path - * parameters using the given {@code descriptors}. The given {@code attributes} will - * be included in the model during template rendering. If - * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will - * be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected PathParametersSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedParameters) { - super("path-parameters", descriptors, attributes, ignoreUndocumentedParameters); - } - - @Override - protected Map createModel(Operation operation) { - Map model = super.createModel(operation); - model.put("path", removeQueryStringIfPresent(extractUrlTemplate(operation))); - return model; - } - - private String removeQueryStringIfPresent(String urlTemplate) { - int index = urlTemplate.indexOf('?'); - if (index == -1) { - return urlTemplate; - } - return urlTemplate.substring(0, index); - } - - @Override - protected Set extractActualParameters(Operation operation) { - String urlTemplate = removeQueryStringIfPresent(extractUrlTemplate(operation)); - Matcher matcher = NAMES_PATTERN.matcher(urlTemplate); - Set actualParameters = new HashSet<>(); - while (matcher.find()) { - String match = matcher.group(1); - actualParameters.add(getParameterName(match)); - } - return actualParameters; - } - - private String extractUrlTemplate(Operation operation) { - String urlTemplate = (String) operation.getAttributes() - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE); - Assert.notNull(urlTemplate, "urlTemplate not found. If you are using MockMvc did " - + "you use RestDocumentationRequestBuilders to build the request?"); - return urlTemplate; - } - - private static String getParameterName(String match) { - int colonIndex = match.indexOf(':'); - return (colonIndex != -1) ? match.substring(0, colonIndex) : match; - } - - @Override - protected void verificationFailed(Set undocumentedParameters, Set missingParameters) { - String message = ""; - if (!undocumentedParameters.isEmpty()) { - message += "Path parameters with the following names were not documented: " + undocumentedParameters; - } - if (!missingParameters.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Path parameters with the following names were not found in " + "the request: " - + missingParameters; - } - throw new SnippetException(message); - } - - /** - * Returns a new {@code PathParametersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final PathParametersSnippet and(ParameterDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code PathParametersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final PathParametersSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(getParameterDescriptors().values()); - combinedDescriptors.addAll(additionalDescriptors); - return new PathParametersSnippet(combinedDescriptors, this.getAttributes(), isIgnoreUndocumentedParameters()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/QueryParametersSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/QueryParametersSnippet.java deleted file mode 100644 index 2485c2f3d..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/QueryParametersSnippet.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.QueryParameters; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; - -/** - * A {@link Snippet} that documents the query parameters supported by a RESTful resource. - * - * @author Andy Wilkinson - * @since 3.0.0 - * @see RequestDocumentation#queryParameters(ParameterDescriptor...) - * @see RequestDocumentation#queryParameters(Map, ParameterDescriptor...) - */ -public class QueryParametersSnippet extends AbstractParametersSnippet { - - /** - * Creates a new {@code QueryParametersSnippet} that will document the request's query - * parameters using the given {@code descriptors}. Undocumented parameters will - * trigger a failure. - * @param descriptors the parameter descriptors - */ - protected QueryParametersSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code QueryParametersSnippet} that will document the request's query - * parameters using the given {@code descriptors}. If - * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will - * be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected QueryParametersSnippet(List descriptors, boolean ignoreUndocumentedParameters) { - this(descriptors, null, ignoreUndocumentedParameters); - } - - /** - * Creates a new {@code QueryParametersSnippet} that will document the request's query - * parameters using the given {@code descriptors}. The given {@code attributes} will - * be included in the model during template rendering. Undocumented parameters will - * trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - */ - protected QueryParametersSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code QueryParametersSnippet} that will document the request's query - * parameters using the given {@code descriptors}. The given {@code attributes} will - * be included in the model during template rendering. If - * {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will - * be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedParameters whether undocumented parameters should be - * ignored - */ - protected QueryParametersSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedParameters) { - super("query-parameters", descriptors, attributes, ignoreUndocumentedParameters); - } - - @Override - protected void verificationFailed(Set undocumentedParameters, Set missingParameters) { - String message = ""; - if (!undocumentedParameters.isEmpty()) { - message += "Query parameters with the following names were not documented: " + undocumentedParameters; - } - if (!missingParameters.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Query parameters with the following names were not found in the request: " + missingParameters; - } - throw new SnippetException(message); - } - - @Override - protected Set extractActualParameters(Operation operation) { - return QueryParameters.from(operation.getRequest()).keySet(); - } - - /** - * Returns a new {@code QueryParametersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public QueryParametersSnippet and(ParameterDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code QueryParametersSnippet} configured with this snippet's - * attributes and its descriptors combined with the given - * {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public QueryParametersSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(getParameterDescriptors().values()); - combinedDescriptors.addAll(additionalDescriptors); - return new QueryParametersSnippet(combinedDescriptors, this.getAttributes(), - this.isIgnoreUndocumentedParameters()); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java deleted file mode 100644 index 221006b83..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java +++ /dev/null @@ -1,669 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.springframework.restdocs.operation.OperationRequest; - -/** - * Static factory methods for documenting aspects of a request sent to a RESTful API. - * - * @author Andy Wilkinson - * @author Marcel Overdijk - */ -public abstract class RequestDocumentation { - - private RequestDocumentation() { - - } - - /** - * Creates a {@link ParameterDescriptor} that describes a request or path parameter - * with the given {@code name}. - * @param name the name of the parameter - * @return a {@link ParameterDescriptor} ready for further configuration - */ - public static ParameterDescriptor parameterWithName(String name) { - return new ParameterDescriptor(name); - } - - /** - * Creates a {@link RequestPartDescriptor} that describes a request part with the - * given {@code name}. - * @param name the name of the request part - * @return a {@link RequestPartDescriptor} ready for further configuration - */ - public static RequestPartDescriptor partWithName(String name) { - return new RequestPartDescriptor(name); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The parameters will be documented using the given - * {@code descriptors}. - *

- * If a parameter is present in the request path, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * parameter is documented, is not marked as optional, and is not present in the - * request path, a failure will also occur. - *

- * If you do not want to document a path parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet pathParameters(ParameterDescriptor... descriptors) { - return pathParameters(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The parameters will be documented using the given - * {@code descriptors}. - *

- * If a parameter is present in the request path, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * parameter is documented, is not marked as optional, and is not present in the - * request path, a failure will also occur. - *

- * If you do not want to document a path parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet pathParameters(List descriptors) { - return new PathParametersSnippet(descriptors); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The parameters will be documented using the given - * {@code descriptors}. - *

- * If a parameter is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented parameters will be ignored. - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet relaxedPathParameters(ParameterDescriptor... descriptors) { - return relaxedPathParameters(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The parameters will be documented using the given - * {@code descriptors}. - *

- * If a parameter is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented parameters will be ignored. - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet relaxedPathParameters(List descriptors) { - return new PathParametersSnippet(descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the parameters will be documented using the given {@code descriptors} - * . - *

- * If a parameter is present in the request path, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * parameter is documented, is not marked as optional, and is not present in the - * request path, a failure will also occur. - *

- * If you do not want to document a path parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet pathParameters(Map attributes, - ParameterDescriptor... descriptors) { - return pathParameters(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the parameters will be documented using the given {@code descriptors} - * . - *

- * If a parameter is present in the request path, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * parameter is documented, is not marked as optional, and is not present in the - * request path, a failure will also occur. - *

- * If you do not want to document a path parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet pathParameters(Map attributes, - List descriptors) { - return new PathParametersSnippet(descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the parameters will be documented using the given {@code descriptors} - * . - *

- * If a parameter is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented parameters will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet relaxedPathParameters(Map attributes, - ParameterDescriptor... descriptors) { - return relaxedPathParameters(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the path parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the parameters will be documented using the given {@code descriptors} - * . - *

- * If a parameter is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented parameters will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the parameters in the request's path - * @return the snippet that will document the parameters - */ - public static PathParametersSnippet relaxedPathParameters(Map attributes, - List descriptors) { - return new PathParametersSnippet(descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The query parameters will be documented using the given - * {@code descriptors}. - *

- * If a query parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * query parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a query parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the request's query parameters - * @return the snippet - * @since 3.0.0 - */ - public static QueryParametersSnippet queryParameters(ParameterDescriptor... descriptors) { - return queryParameters(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The query parameters will be documented using the given - * {@code descriptors}. - *

- * If a query parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * query parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a query parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the request's query parameters - * @return the snippet - * @since 3.0.0 - */ - public static QueryParametersSnippet queryParameters(List descriptors) { - return new QueryParametersSnippet(descriptors); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The query parameters will be documented using the given - * {@code descriptors}. - *

- * If a query parameter is documented, is not marked as optional, and is not present - * in the response, a failure will occur. Any undocumented query parameters will be - * ignored. - * @param descriptors the descriptions of the request's query parameters - * @return the snippet - * @since 3.0.0 - */ - public static QueryParametersSnippet relaxedQueryParameters(ParameterDescriptor... descriptors) { - return relaxedQueryParameters(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The query parameters will be documented using the given - * {@code descriptors}. - *

- * If a parameter is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented query parameters will be ignored. - * @param descriptors the descriptions of the request's query parameters - * @return the snippet - * @since 3.0.0 - */ - public static QueryParametersSnippet relaxedQueryParameters(List descriptors) { - return new QueryParametersSnippet(descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the query parameters will be documented using the given - * {@code descriptors} . - *

- * If a query parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * query parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a query parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the request's query parameters - * @return the snippet that will document the query parameters - * @since 3.0.0 - */ - public static QueryParametersSnippet queryParameters(Map attributes, - ParameterDescriptor... descriptors) { - return queryParameters(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the query parameters will be documented using the given - * {@code descriptors} . - *

- * If a query parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a - * query parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a query parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the request's query parameters - * @return the snippet that will document the query parameters - * @since 3.0.0 - */ - public static QueryParametersSnippet queryParameters(Map attributes, - List descriptors) { - return new QueryParametersSnippet(descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the query parameters will be documented using the given - * {@code descriptors} . - *

- * If a query parameter is documented, is not marked as optional, and is not present - * in the response, a failure will occur. Any undocumented query parameters will be - * ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's query parameters - * @return the snippet that will document the query parameters - * @since 3.0.0 - */ - public static QueryParametersSnippet relaxedQueryParameters(Map attributes, - ParameterDescriptor... descriptors) { - return relaxedQueryParameters(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the query parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the query parameters will be documented using the given - * {@code descriptors} . - *

- * If a query parameter is documented, is not marked as optional, and is not present - * in the response, a failure will occur. Any undocumented query parameters will be - * ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's query parameters - * @return the snippet that will document the query parameters - * @since 3.0.0 - */ - public static QueryParametersSnippet relaxedQueryParameters(Map attributes, - List descriptors) { - return new QueryParametersSnippet(descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The form parameters will be documented using the given - * {@code descriptors}. - *

- * If a form parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a form - * parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a form parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the request's form parameters - * @return the snippet - * @since 3.0.0 - */ - public static FormParametersSnippet formParameters(ParameterDescriptor... descriptors) { - return formParameters(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The form parameters will be documented using the given - * {@code descriptors}. - *

- * If a form parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a form - * parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a form parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the request's form parameters - * @return the snippet - * @since 3.0.0 - */ - public static FormParametersSnippet formParameters(List descriptors) { - return new FormParametersSnippet(descriptors); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The form parameters will be documented using the given - * {@code descriptors}. - *

- * If a form parameter is documented, is not marked as optional, and is not present in - * the response, a failure will occur. Any undocumented form parameters will be - * ignored. - * @param descriptors the descriptions of the request's form parameters - * @return the snippet - * @since 3.0.0 - */ - public static FormParametersSnippet relaxedFormParameters(ParameterDescriptor... descriptors) { - return relaxedFormParameters(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The form parameters will be documented using the given - * {@code descriptors}. - *

- * If a parameter is documented, is not marked as optional, and is not present in the - * response, a failure will occur. Any undocumented form parameters will be ignored. - * @param descriptors the descriptions of the request's form parameters - * @return the snippet - * @since 3.0.0 - */ - public static FormParametersSnippet relaxedFormParameters(List descriptors) { - return new FormParametersSnippet(descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the form parameters will be documented using the given - * {@code descriptors} . - *

- * If a form parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a form - * parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a form parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the request's form parameters - * @return the snippet that will document the form parameters - * @since 3.0.0 - */ - public static FormParametersSnippet formParameters(Map attributes, - ParameterDescriptor... descriptors) { - return formParameters(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the form parameters will be documented using the given - * {@code descriptors} . - *

- * If a form parameter is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a form - * parameter is documented, is not marked as optional, and is not present in the - * request, a failure will also occur. - *

- * If you do not want to document a form parameter, a parameter descriptor can be - * marked as {@link ParameterDescriptor#ignored()}. This will prevent it from - * appearing in the generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the request's form parameters - * @return the snippet that will document the form parameters - * @since 3.0.0 - */ - public static FormParametersSnippet formParameters(Map attributes, - List descriptors) { - return new FormParametersSnippet(descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the form parameters will be documented using the given - * {@code descriptors} . - *

- * If a form parameter is documented, is not marked as optional, and is not present in - * the response, a failure will occur. Any undocumented form parameters will be - * ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's form parameters - * @return the snippet that will document the form parameters - * @since 3.0.0 - */ - public static FormParametersSnippet relaxedFormParameters(Map attributes, - ParameterDescriptor... descriptors) { - return relaxedFormParameters(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the form parameters from the API - * operation's request. The given {@code attributes} will be available during snippet - * rendering and the form parameters will be documented using the given - * {@code descriptors} . - *

- * If a form parameter is documented, is not marked as optional, and is not present in - * the response, a failure will occur. Any undocumented form parameters will be - * ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's form parameters - * @return the snippet that will document the form parameters - * @since 3.0.0 - */ - public static FormParametersSnippet relaxedFormParameters(Map attributes, - List descriptors) { - return new FormParametersSnippet(descriptors, attributes, true); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The parts will be documented using the given {@code descriptors}. - *

- * If a part is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a part - * is documented, is not marked as optional, and is not present in the request, a - * failure will also occur. - *

- * If you do not want to document a part, a part descriptor can be marked as - * {@link RequestPartDescriptor#ignored()}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the request's parts - * @return the snippet - * @see OperationRequest#getParts() - */ - public static RequestPartsSnippet requestParts(RequestPartDescriptor... descriptors) { - return requestParts(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The parts will be documented using the given {@code descriptors}. - *

- * If a part is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a part - * is documented, is not marked as optional, and is not present in the request, a - * failure will also occur. - *

- * If you do not want to document a part, a part descriptor can be marked as - * {@link RequestPartDescriptor#ignored()}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - * @param descriptors the descriptions of the request's parts - * @return the snippet - * @see OperationRequest#getParts() - */ - public static RequestPartsSnippet requestParts(List descriptors) { - return new RequestPartsSnippet(descriptors); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The parameters will be documented using the given {@code descriptors}. - *

- * If a part is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented parts will be ignored. - * @param descriptors the descriptions of the request's parts - * @return the snippet - * @see OperationRequest#getParts() - */ - public static RequestPartsSnippet relaxedRequestParts(RequestPartDescriptor... descriptors) { - return relaxedRequestParts(Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The parameters will be documented using the given {@code descriptors}. - *

- * If a part is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented parts will be ignored. - * @param descriptors the descriptions of the request's parts - * @return the snippet - * @see OperationRequest#getParts() - */ - public static RequestPartsSnippet relaxedRequestParts(List descriptors) { - return new RequestPartsSnippet(descriptors, true); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The given {@code attributes} will be available during snippet rendering - * and the parts will be documented using the given {@code descriptors}. - *

- * If a part is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a part - * is documented, is not marked as optional, and is not present in the request, a - * failure will also occur. - *

- * If you do not want to document a part, a part descriptor can be marked as - * {@link RequestPartDescriptor#ignored()}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the request's parts - * @return the snippet - * @see OperationRequest#getParts() - */ - public static RequestPartsSnippet requestParts(Map attributes, - RequestPartDescriptor... descriptors) { - return requestParts(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The given {@code attributes} will be available during snippet rendering - * and the parts will be documented using the given {@code descriptors}. - *

- * If a part is present in the request, but is not documented by one of the - * descriptors, a failure will occur when the snippet is invoked. Similarly, if a part - * is documented, is not marked as optional, and is not present in the request, a - * failure will also occur. - *

- * If you do not want to document a part, a part descriptor can be marked as - * {@link RequestPartDescriptor#ignored()}. This will prevent it from appearing in the - * generated snippet while avoiding the failure described above. - * @param attributes the attributes - * @param descriptors the descriptions of the request's parts - * @return the snippet - * @see OperationRequest#getParts() - */ - public static RequestPartsSnippet requestParts(Map attributes, - List descriptors) { - return new RequestPartsSnippet(descriptors, attributes); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The given {@code attributes} will be available during snippet rendering - * and the parts will be documented using the given {@code descriptors}. - *

- * If a part is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented parts will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's parts - * @return the snippet - */ - public static RequestPartsSnippet relaxedRequestParts(Map attributes, - RequestPartDescriptor... descriptors) { - return relaxedRequestParts(attributes, Arrays.asList(descriptors)); - } - - /** - * Returns a {@code Snippet} that will document the parts from the API operation's - * request. The given {@code attributes} will be available during snippet rendering - * and the parts will be documented using the given {@code descriptors}. - *

- * If a part is documented, is not marked as optional, and is not present in the - * request, a failure will occur. Any undocumented parts will be ignored. - * @param attributes the attributes - * @param descriptors the descriptions of the request's parts - * @return the snippet - */ - public static RequestPartsSnippet relaxedRequestParts(Map attributes, - List descriptors) { - return new RequestPartsSnippet(descriptors, attributes, true); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestPartDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestPartDescriptor.java deleted file mode 100644 index fe1f83ec9..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestPartDescriptor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import org.springframework.restdocs.snippet.IgnorableDescriptor; - -/** - * A descriptor of a request part. - * - * @author Andy Wilkinson - * @since 1.1.0 - * @see RequestDocumentation#partWithName - */ -public class RequestPartDescriptor extends IgnorableDescriptor { - - private final String name; - - private boolean optional; - - /** - * Creates a new {@code RequestPartDescriptor} describing the request part with the - * given {@code name}. - * @param name the name of the request part - */ - protected RequestPartDescriptor(String name) { - this.name = name; - } - - /** - * Marks the request part as optional. - * @return {@code this} - */ - public final RequestPartDescriptor optional() { - this.optional = true; - return this; - } - - /** - * Returns the name of the request part being described by this descriptor. - * @return the name of the parameter - */ - public final String getName() { - return this.name; - } - - /** - * Returns {@code true} if the described request part is optional, otherwise - * {@code false}. - * @return {@code true} if the described request part is optional, otherwise - * {@code false} - */ - public final boolean isOptional() { - return this.optional; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestPartsSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestPartsSnippet.java deleted file mode 100644 index 28302de7b..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/RequestPartsSnippet.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.snippet.TemplatedSnippet; -import org.springframework.util.Assert; - -/** - * A {@link Snippet} that documents the request parts supported by a RESTful resource. - * - * @author Andy Wilkinson - * @since 1.1.0 - * @see RequestDocumentation#requestParts(RequestPartDescriptor...) - * @see RequestDocumentation#requestParts(Map, RequestPartDescriptor...) - * @see RequestDocumentation#relaxedRequestParts(RequestPartDescriptor...) - * @see RequestDocumentation#relaxedRequestParts(Map, RequestPartDescriptor...) - */ -public class RequestPartsSnippet extends TemplatedSnippet { - - private final Map descriptorsByName = new LinkedHashMap<>(); - - private final boolean ignoreUndocumentedParts; - - /** - * Creates a new {@code RequestPartsSnippet} that will document the request's parts - * using the given {@code descriptors}. Undocumented parts will trigger a failure. - * @param descriptors the parameter descriptors - */ - protected RequestPartsSnippet(List descriptors) { - this(descriptors, null, false); - } - - /** - * Creates a new {@code RequestPartsSnippet} that will document the request's parts - * using the given {@code descriptors}. If {@code ignoreUndocumentedParts} is - * {@code true}, undocumented parts will be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param ignoreUndocumentedParts whether undocumented parts should be ignored - */ - protected RequestPartsSnippet(List descriptors, boolean ignoreUndocumentedParts) { - this(descriptors, null, ignoreUndocumentedParts); - } - - /** - * Creates a new {@code RequestPartsSnippet} that will document the request's parts - * using the given {@code descriptors}. The given {@code attributes} will be included - * in the model during template rendering. Undocumented parts will trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - */ - protected RequestPartsSnippet(List descriptors, Map attributes) { - this(descriptors, attributes, false); - } - - /** - * Creates a new {@code RequestPartsSnippet} that will document the request's parts - * using the given {@code descriptors}. The given {@code attributes} will be included - * in the model during template rendering. If {@code ignoreUndocumentedParts} is - * {@code true}, undocumented parts will be ignored and will not trigger a failure. - * @param descriptors the parameter descriptors - * @param attributes the additional attributes - * @param ignoreUndocumentedParts whether undocumented parts should be ignored - */ - protected RequestPartsSnippet(List descriptors, Map attributes, - boolean ignoreUndocumentedParts) { - super("request-parts", attributes); - for (RequestPartDescriptor descriptor : descriptors) { - Assert.notNull(descriptor.getName(), "Request part descriptors must have a name"); - if (!descriptor.isIgnored()) { - Assert.notNull(descriptor.getDescription(), "The descriptor for request part '" + descriptor.getName() - + "' must either have a description or be marked as " + "ignored"); - } - this.descriptorsByName.put(descriptor.getName(), descriptor); - } - this.ignoreUndocumentedParts = ignoreUndocumentedParts; - } - - /** - * Returns a new {@code RequestPartsSnippet} configured with this snippet's attributes - * and its descriptors combined with the given {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestPartsSnippet and(RequestPartDescriptor... additionalDescriptors) { - return and(Arrays.asList(additionalDescriptors)); - } - - /** - * Returns a new {@code RequestPartsSnippet} configured with this snippet's attributes - * and its descriptors combined with the given {@code additionalDescriptors}. - * @param additionalDescriptors the additional descriptors - * @return the new snippet - */ - public final RequestPartsSnippet and(List additionalDescriptors) { - List combinedDescriptors = new ArrayList<>(this.descriptorsByName.values()); - combinedDescriptors.addAll(additionalDescriptors); - return new RequestPartsSnippet(combinedDescriptors, this.getAttributes()); - } - - @Override - protected Map createModel(Operation operation) { - verifyRequestPartDescriptors(operation); - Map model = new HashMap<>(); - List> requestParts = new ArrayList<>(); - for (Entry entry : this.descriptorsByName.entrySet()) { - RequestPartDescriptor descriptor = entry.getValue(); - if (!descriptor.isIgnored()) { - requestParts.add(createModelForDescriptor(descriptor)); - } - } - model.put("requestParts", requestParts); - return model; - } - - private void verifyRequestPartDescriptors(Operation operation) { - Set actualRequestParts = extractActualRequestParts(operation); - Set expectedRequestParts = new HashSet<>(); - for (Entry entry : this.descriptorsByName.entrySet()) { - if (!entry.getValue().isOptional()) { - expectedRequestParts.add(entry.getKey()); - } - } - Set undocumentedRequestParts; - if (this.ignoreUndocumentedParts) { - undocumentedRequestParts = Collections.emptySet(); - } - else { - undocumentedRequestParts = new HashSet<>(actualRequestParts); - undocumentedRequestParts.removeAll(this.descriptorsByName.keySet()); - } - - Set missingRequestParts = new HashSet<>(expectedRequestParts); - missingRequestParts.removeAll(actualRequestParts); - - if (!undocumentedRequestParts.isEmpty() || !missingRequestParts.isEmpty()) { - verificationFailed(undocumentedRequestParts, missingRequestParts); - } - } - - private Set extractActualRequestParts(Operation operation) { - Set actualRequestParts = new HashSet<>(); - for (OperationRequestPart requestPart : operation.getRequest().getParts()) { - actualRequestParts.add(requestPart.getName()); - } - return actualRequestParts; - } - - private void verificationFailed(Set undocumentedRequestParts, Set missingRequestParts) { - String message = ""; - if (!undocumentedRequestParts.isEmpty()) { - message += "Request parts with the following names were not documented: " + undocumentedRequestParts; - } - if (!missingRequestParts.isEmpty()) { - if (message.length() > 0) { - message += ". "; - } - message += "Request parts with the following names were not found in " + "the request: " - + missingRequestParts; - } - throw new SnippetException(message); - } - - private Map createModelForDescriptor(RequestPartDescriptor descriptor) { - Map model = new HashMap<>(); - model.put("name", descriptor.getName()); - model.put("description", descriptor.getDescription()); - model.put("optional", descriptor.isOptional()); - model.putAll(descriptor.getAttributes()); - return model; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/package-info.java deleted file mode 100644 index 36017ad3f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/request/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Documenting query and path parameters of requests sent to a RESTful API. - */ -package org.springframework.restdocs.request; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/AbstractDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/AbstractDescriptor.java deleted file mode 100644 index 0df6ecbc1..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/AbstractDescriptor.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.restdocs.snippet.Attributes.Attribute; - -/** - * Base class for descriptors. Provides the ability to associate arbitrary attributes with - * a descriptor. - * - * @param the type of the descriptor - * @author Andy Wilkinson - */ -public abstract class AbstractDescriptor> { - - private Map attributes = new HashMap<>(); - - private Object description; - - /** - * Adds the given {@code attributes} to the descriptor. - * @param attributes the attributes - * @return the descriptor - */ - @SuppressWarnings("unchecked") - public final T attributes(Attribute... attributes) { - for (Attribute attribute : attributes) { - this.attributes.put(attribute.getKey(), attribute.getValue()); - } - return (T) this; - } - - /** - * Specifies the description. - * @param description the description - * @return the descriptor - */ - @SuppressWarnings("unchecked") - public final T description(Object description) { - this.description = description; - return (T) this; - } - - /** - * Returns the description. - * @return the description - */ - public final Object getDescription() { - return this.description; - } - - /** - * Returns the descriptor's attributes. - * @return the attributes - */ - public final Map getAttributes() { - return this.attributes; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/Attributes.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/Attributes.java deleted file mode 100644 index d49131638..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/Attributes.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.util.HashMap; -import java.util.Map; - -/** - * A fluent API for building a map of attributes. - * - * @author Andy Wilkinson - */ -public abstract class Attributes { - - private Attributes() { - - } - - /** - * Creates an attribute with the given {@code key}. A value for the attribute must - * still be specified. - * @param key the key of the attribute - * @return an {@code AttributeBuilder} to use to specify the value of the attribute - * @see AttributeBuilder#value(Object) - */ - public static AttributeBuilder key(String key) { - return new AttributeBuilder(key); - } - - /** - * Creates a {@code Map} of the given {@code attributes}. - * @param attributes the attributes - * @return a Map of the attributes - */ - public static Map attributes(Attribute... attributes) { - Map attributeMap = new HashMap<>(); - for (Attribute attribute : attributes) { - attributeMap.put(attribute.getKey(), attribute.getValue()); - } - return attributeMap; - } - - /** - * A simple builder for an attribute (key-value pair). - */ - public static final class AttributeBuilder { - - private final String key; - - private AttributeBuilder(String key) { - this.key = key; - } - - /** - * Configures the value of the attribute. - * @param value the attribute's value - * @return a newly created {@code Attribute} - */ - public Attribute value(Object value) { - return new Attribute(this.key, value); - } - - } - - /** - * An attribute (key-value pair). - */ - public static final class Attribute { - - private final String key; - - private final Object value; - - /** - * Creates a new attribute with the given {@code key} and {@code value}. - * @param key the key - * @param value the value - */ - public Attribute(String key, Object value) { - this.key = key; - this.value = value; - } - - /** - * Returns the attribute's key. - * @return the key - */ - public String getKey() { - return this.key; - } - - /** - * Returns the attribute's value. - * @return the value - */ - public Object getValue() { - return this.value; - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/IgnorableDescriptor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/IgnorableDescriptor.java deleted file mode 100644 index d3ba77250..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/IgnorableDescriptor.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -/** - * Base class for descriptors for items that can be ignored. - * - * @param the type of the descriptor - * @author Andy Wilkinson - */ -public abstract class IgnorableDescriptor> extends AbstractDescriptor { - - private boolean ignored = false; - - /** - * Marks the described item as being ignored. Ignored items are not included in the - * generated documentation. - * @return the descriptor - */ - @SuppressWarnings("unchecked") - public final T ignored() { - this.ignored = true; - return (T) this; - } - - /** - * Returns whether or not the item being described should be ignored and, therefore, - * should not be included in the documentation. - * @return {@code true} if the item should be ignored, otherwise {@code false}. - */ - public final boolean isIgnored() { - return this.ignored; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/ModelCreationException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/ModelCreationException.java deleted file mode 100644 index 8ae827247..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/ModelCreationException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -/** - * An exception that can be thrown by a {@link TemplatedSnippet} to indicate that a - * failure has occurred during model creation. - * - * @author Andy Wilkinson - * @see TemplatedSnippet#createModel(org.springframework.restdocs.operation.Operation) - */ -@SuppressWarnings("serial") -public class ModelCreationException extends RuntimeException { - - /** - * Creates a new {@code ModelCreationException} with the given {@code cause}. - * @param cause the cause - */ - public ModelCreationException(Throwable cause) { - super(cause); - } - - /** - * Creates a new {@code ModelCreationException} with the given {@code message} and - * {@code cause}. - * @param message the message - * @param cause the cause - */ - public ModelCreationException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/PlaceholderResolverFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/PlaceholderResolverFactory.java deleted file mode 100644 index 4f51238bb..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/PlaceholderResolverFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; - -/** - * A factory for creating {@link PlaceholderResolver} instances. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public interface PlaceholderResolverFactory { - - /** - * Creates a new {@link PlaceholderResolver} using the given {@code context}. - * @param context the context - * @return the placeholder resolver - */ - PlaceholderResolver create(RestDocumentationContext context); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java deleted file mode 100644 index df8a743b1..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolver.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2014-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; - -/** - * A {@link PlaceholderResolver} that resolves placeholders using a - * {@link RestDocumentationContext}. The following placeholders are supported: - *

    - *
  • {@code step} - the {@link RestDocumentationContext#getStepCount() step current - * count}. - *
  • {@code methodName} - the unmodified name of the - * {@link RestDocumentationContext#getTestMethodName() current test method} without - * applying any formatting - *
  • {@code method-name} - the name of the - * {@link RestDocumentationContext#getTestMethodName() current test method} formatted - * using kebab-case - *
  • {@code method_name} - the name of the - * {@link RestDocumentationContext#getTestMethodName() current test method} formatted - * using snake_case - *
  • {@code ClassName} - the unmodified {@link Class#getSimpleName() simple name} of the - * {@link RestDocumentationContext#getTestClass() current test class} - *
  • {@code class-name} - the {@link Class#getSimpleName() simple name} of the - * {@link RestDocumentationContext#getTestClass() current test class} formatted using - * kebab-case - *
  • {@code class_name} - the {@link Class#getSimpleName() simple name} of the - * {@link RestDocumentationContext#getTestClass() current test class} formatted using - * snake case - *
- * - * @author Andy Wilkinson - */ -public class RestDocumentationContextPlaceholderResolver implements PlaceholderResolver { - - private final RestDocumentationContext context; - - /** - * Creates a new placeholder resolver that will resolve placeholders using the given - * {@code context}. - * @param context the context to use - */ - public RestDocumentationContextPlaceholderResolver(RestDocumentationContext context) { - this.context = context; - } - - @Override - public String resolvePlaceholder(String placeholderName) { - if ("step".equals(placeholderName)) { - return Integer.toString(this.context.getStepCount()); - } - String converted = tryMethodNameConversion(placeholderName); - if (converted != null) { - return converted; - } - return tryClassNameConversion(placeholderName); - } - - private String tryMethodNameConversion(String placeholderName) { - if ("methodName".equals(placeholderName)) { - return this.context.getTestMethodName(); - } - if ("method-name".equals(placeholderName)) { - return camelCaseToKebabCase(this.context.getTestMethodName()); - } - if ("method_name".equals(placeholderName)) { - return camelCaseToSnakeCase(this.context.getTestMethodName()); - } - return null; - } - - private String tryClassNameConversion(String placeholderName) { - if ("ClassName".equals(placeholderName)) { - return this.context.getTestClass().getSimpleName(); - } - if ("class-name".equals(placeholderName)) { - return camelCaseToKebabCase(this.context.getTestClass().getSimpleName()); - } - if ("class_name".equals(placeholderName)) { - return camelCaseToSnakeCase(this.context.getTestClass().getSimpleName()); - } - return null; - } - - /** - * Converts the given {@code string} from camelCase to kebab-case. - * @param string the string - * @return the converted string - */ - protected final String camelCaseToKebabCase(String string) { - return camelCaseToSeparator(string, "-"); - } - - /** - * Converts the given {@code string} from camelCase to snake_case. - * @param string the string - * @return the converted string - */ - protected final String camelCaseToSnakeCase(String string) { - return camelCaseToSeparator(string, "_"); - } - - /** - * Returns the {@link RestDocumentationContext} that should be used during placeholder - * resolution. - * @return the context - */ - protected final RestDocumentationContext getContext() { - return this.context; - } - - private String camelCaseToSeparator(String string, String separator) { - StringBuffer result = new StringBuffer(); - char[] chars = string.toCharArray(); - for (int i = 0; i < chars.length; i++) { - char current = chars[i]; - if (Character.isUpperCase(current) && i > 0) { - if (Character.isLowerCase(chars[i - 1]) - || (i < chars.length - 1 && Character.isLowerCase(chars[i + 1]))) { - result.append(separator); - } - } - result.append(Character.toLowerCase(chars[i])); - } - return result.toString(); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverFactory.java deleted file mode 100644 index 852b9b8d0..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; - -/** - * A {@link PlaceholderResolverFactory} that creates - * {@link RestDocumentationContextPlaceholderResolver} instances. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public final class RestDocumentationContextPlaceholderResolverFactory implements PlaceholderResolverFactory { - - @Override - public PlaceholderResolver create(RestDocumentationContext context) { - return new RestDocumentationContextPlaceholderResolver(context); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/Snippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/Snippet.java deleted file mode 100644 index c4c4294cc..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/Snippet.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.io.IOException; - -import org.springframework.restdocs.operation.Operation; - -/** - * A {@link Snippet} is used to document aspects of a call to a RESTful API. - * - * @author Andy Wilkinson - */ -public interface Snippet { - - /** - * Documents the call to the RESTful API described by the given {@code operation}. - * @param operation the API operation - * @throws IOException if a failure occurs will documenting the operation - */ - void document(Operation operation) throws IOException; - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/SnippetException.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/SnippetException.java deleted file mode 100644 index 909acf175..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/SnippetException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -/** - * A {@link RuntimeException} thrown to indicate a problem with the generation of a - * documentation snippet. - * - * @author Andy Wilkinson - */ -@SuppressWarnings("serial") -public class SnippetException extends RuntimeException { - - /** - * Creates a new {@code SnippetException} described by the given {@code message}. - * @param message the message that describes the problem - */ - public SnippetException(String message) { - super(message); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java deleted file mode 100644 index 2310be302..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/StandardWriterResolver.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.util.PropertyPlaceholderHelper; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; - -/** - * Standard implementation of {@link WriterResolver}. - * - * @author Andy Wilkinson - */ -public final class StandardWriterResolver implements WriterResolver { - - private final PlaceholderResolverFactory placeholderResolverFactory; - - private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("{", "}"); - - private String encoding = "UTF-8"; - - private TemplateFormat templateFormat; - - /** - * Creates a new {@code StandardWriterResolver} that will use a - * {@link PlaceholderResolver} created from the given - * {@code placeholderResolverFactory} to resolve any placeholders in the - * {@code operationName}. Writers will use the given {@code encoding} and, when - * writing to a file, will use a filename appropriate for content generated from - * templates in the given {@code templateFormat}. - * @param placeholderResolverFactory the placeholder resolver factory - * @param encoding the encoding - * @param templateFormat the snippet format - */ - public StandardWriterResolver(PlaceholderResolverFactory placeholderResolverFactory, String encoding, - TemplateFormat templateFormat) { - this.placeholderResolverFactory = placeholderResolverFactory; - this.encoding = encoding; - this.templateFormat = templateFormat; - } - - @Override - public Writer resolve(String operationName, String snippetName, RestDocumentationContext context) - throws IOException { - PlaceholderResolver placeholderResolver = this.placeholderResolverFactory.create(context); - String outputDirectory = replacePlaceholders(placeholderResolver, operationName); - String fileName = replacePlaceholders(placeholderResolver, snippetName) + "." - + this.templateFormat.getFileExtension(); - File outputFile = resolveFile(outputDirectory, fileName, context); - if (outputFile != null) { - createDirectoriesIfNecessary(outputFile); - return new OutputStreamWriter(new FileOutputStream(outputFile), this.encoding); - } - else { - return new OutputStreamWriter(System.out, this.encoding); - } - } - - private String replacePlaceholders(PlaceholderResolver resolver, String input) { - return this.propertyPlaceholderHelper.replacePlaceholders(input, resolver); - } - - File resolveFile(String outputDirectory, String fileName, RestDocumentationContext context) { - File outputFile = new File(outputDirectory, fileName); - if (!outputFile.isAbsolute()) { - outputFile = makeRelativeToConfiguredOutputDir(outputFile, context); - } - return outputFile; - } - - private File makeRelativeToConfiguredOutputDir(File outputFile, RestDocumentationContext context) { - File configuredOutputDir = context.getOutputDirectory(); - if (configuredOutputDir != null) { - return new File(configuredOutputDir, outputFile.getPath()); - } - return null; - } - - private void createDirectoriesIfNecessary(File outputFile) { - File parent = outputFile.getParentFile(); - if (!parent.isDirectory() && !parent.mkdirs()) { - throw new IllegalStateException("Failed to create directory '" + parent + "'"); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java deleted file mode 100644 index fb89916b4..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/TemplatedSnippet.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.io.IOException; -import java.io.Writer; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.templates.Template; -import org.springframework.restdocs.templates.TemplateEngine; - -/** - * Base class for a {@link Snippet} that is produced using a {@link Template} and - * {@link TemplateEngine}. - * - * @author Andy Wilkinson - */ -public abstract class TemplatedSnippet implements Snippet { - - private final Map attributes = new HashMap<>(); - - private final String snippetName; - - private final String templateName; - - /** - * Creates a new {@code TemplatedSnippet} that will produce a snippet with the given - * {@code snippetName}. The {@code snippetName} will also be used as the name of the - * template. The given {@code attributes} will be included in the model during - * rendering of the template. - * @param snippetName the name of the snippet - * @param attributes the additional attributes - * @see #TemplatedSnippet(String, String, Map) - */ - protected TemplatedSnippet(String snippetName, Map attributes) { - this(snippetName, snippetName, attributes); - } - - /** - * Creates a new {@code TemplatedSnippet} that will produce a snippet with the given - * {@code snippetName} using a template with the given {@code templateName}. The given - * {@code attributes} will be included in the model during rendering of the template. - * @param snippetName the name of the snippet - * @param templateName the name of the template - * @param attributes the additional attributes - */ - protected TemplatedSnippet(String snippetName, String templateName, Map attributes) { - this.templateName = templateName; - this.snippetName = snippetName; - if (attributes != null) { - this.attributes.putAll(attributes); - } - } - - @Override - public void document(Operation operation) throws IOException { - RestDocumentationContext context = (RestDocumentationContext) operation.getAttributes() - .get(RestDocumentationContext.class.getName()); - WriterResolver writerResolver = (WriterResolver) operation.getAttributes().get(WriterResolver.class.getName()); - Map model = createModel(operation); - model.putAll(this.attributes); - try (Writer writer = writerResolver.resolve(operation.getName(), this.snippetName, context)) { - TemplateEngine templateEngine = (TemplateEngine) operation.getAttributes() - .get(TemplateEngine.class.getName()); - writer.append(templateEngine.compileTemplate(this.templateName).render(model)); - } - } - - /** - * Create the model that should be used during template rendering to document the - * given {@code operation}. Any additional attributes that were supplied when this - * {@code TemplatedSnippet} were created will be automatically added to the model - * prior to rendering. - * @param operation the operation - * @return the model - * @throws ModelCreationException if model creation fails - */ - protected abstract Map createModel(Operation operation); - - /** - * Returns the additional attributes that will be included in the model during - * template rendering. - * @return the additional attributes - */ - protected final Map getAttributes() { - return this.attributes; - } - - /** - * Returns the name of the snippet that will be created. - * @return the snippet name - */ - protected final String getSnippetName() { - return this.snippetName; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java deleted file mode 100644 index 90c3577be..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/WriterResolver.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.io.IOException; -import java.io.Writer; - -import org.springframework.restdocs.RestDocumentationContext; - -/** - * A {@code WriterResolver} is used to access the {@link Writer} that should be used to - * write a snippet for an operation that is being documented. - * - * @author Andy Wilkinson - */ -public interface WriterResolver { - - /** - * Returns a writer that can be used to write the snippet with the given name for the - * operation with the given name. - * @param operationName the name of the operation that is being documented - * @param snippetName the name of the snippet - * @param restDocumentationContext the current documentation context - * @return the writer - * @throws IOException if a writer cannot be resolved - */ - Writer resolve(String operationName, String snippetName, RestDocumentationContext restDocumentationContext) - throws IOException; - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/package-info.java deleted file mode 100644 index 89135cc70..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/snippet/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Snippet generation. - */ -package org.springframework.restdocs.snippet; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java deleted file mode 100644 index 087985e6a..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/StandardTemplateResourceResolver.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -/** - * Standard implementation of {@link TemplateResourceResolver}. - *

- * Templates are resolved by looking for resources on the classpath. The following - * locations are checked in order: - *

    - *
  1. - * org/springframework/restdocs/templates/${templateFormatId}/${name}.snippet - *
  2. - *
  3. org/springframework/restdocs/templates/${name}.snippet
  4. - *
  5. - * org/springframework/restdocs/templates/${templateFormatId}/default-${name}.snippet - *
  6. - *
- * - * @author Andy Wilkinson - * @see TemplateFormat#getId() - */ -public class StandardTemplateResourceResolver implements TemplateResourceResolver { - - private final TemplateFormat templateFormat; - - /** - * Creates a new {@code StandardTemplateResourceResolver} that will produce default - * template resources formatted with the given {@code templateFormat}. - * @param templateFormat the format for the default snippet templates - */ - public StandardTemplateResourceResolver(TemplateFormat templateFormat) { - this.templateFormat = templateFormat; - } - - @Override - public Resource resolveTemplateResource(String name) { - Resource formatSpecificCustomTemplate = getFormatSpecificCustomTemplate(name); - if (formatSpecificCustomTemplate.exists()) { - return formatSpecificCustomTemplate; - } - Resource customTemplate = getCustomTemplate(name); - if (customTemplate.exists()) { - return customTemplate; - } - Resource defaultTemplate = getDefaultTemplate(name); - if (defaultTemplate.exists()) { - return defaultTemplate; - } - throw new IllegalStateException("Template named '" + name + "' could not be resolved"); - } - - private Resource getFormatSpecificCustomTemplate(String name) { - return new ClassPathResource(String.format("org/springframework/restdocs/templates/%s/%s.snippet", - this.templateFormat.getId(), name)); - } - - private Resource getCustomTemplate(String name) { - return new ClassPathResource(String.format("org/springframework/restdocs/templates/%s.snippet", name)); - } - - private Resource getDefaultTemplate(String name) { - return new ClassPathResource(String.format("org/springframework/restdocs/templates/%s/default-%s.snippet", - this.templateFormat.getId(), name)); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/Template.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/Template.java deleted file mode 100644 index 182a86505..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/Template.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -import java.util.Map; - -/** - * A compiled {@code Template} that can be rendered to a {@link String}. - * - * @author Andy Wilkinson - * - */ -public interface Template { - - /** - * Renders the template to a {@link String} using the given {@code context} for - * variable/property resolution. - * @param context the context to use - * @return the rendered template - */ - String render(Map context); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateEngine.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateEngine.java deleted file mode 100644 index 5c328d1f6..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateEngine.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -import java.io.IOException; - -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; - -/** - * A {@code TemplateEngine} is used to render documentation snippets. - * - * @author Andy Wilkinson - * @see MustacheTemplateEngine - */ -public interface TemplateEngine { - - /** - * Compiles the template at the given {@code path}. Typically, a - * {@link TemplateResourceResolver} will be used to resolve the path into a resource - * that can be read and compiled. - * @param path the path of the template - * @return the compiled {@code Template} - * @throws IOException if compilation fails - */ - Template compileTemplate(String path) throws IOException; - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateFormat.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateFormat.java deleted file mode 100644 index 820a3cd09..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateFormat.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -/** - * A {@link TemplateFormat} provides information about a particular template format, such - * as Asciidoctor or Markdown. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public interface TemplateFormat { - - /** - * Returns the id of this template format. - * @return the id - */ - String getId(); - - /** - * Returns the file extension to use for files generated from templates in this - * format. - * @return the file extension - */ - String getFileExtension(); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateFormats.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateFormats.java deleted file mode 100644 index aeb7c1452..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateFormats.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -/** - * An enumeration of the built-in formats for which templates are provided. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public abstract class TemplateFormats { - - private static final TemplateFormat ASCIIDOCTOR = new AsciidoctorTemplateFormat(); - - private static final TemplateFormat MARKDOWN = new MarkdownTemplateFormat(); - - private TemplateFormats() { - - } - - /** - * Returns the Asciidoctor template format with the ID {@code asciidoctor} and the - * file extension {@code adoc}. - * @return the template format - */ - public static TemplateFormat asciidoctor() { - return ASCIIDOCTOR; - } - - /** - * Returns the Markdown template format with the ID {@code markdown} and the file - * extension {@code md}. - * @return the template format - */ - public static TemplateFormat markdown() { - return MARKDOWN; - } - - private abstract static class AbstractTemplateFormat implements TemplateFormat { - - private final String name; - - private final String fileExtension; - - private AbstractTemplateFormat(String name, String fileExtension) { - this.name = name; - this.fileExtension = fileExtension; - } - - @Override - public String getId() { - return this.name; - } - - @Override - public String getFileExtension() { - return this.fileExtension; - } - - } - - private static final class AsciidoctorTemplateFormat extends AbstractTemplateFormat { - - private AsciidoctorTemplateFormat() { - super("asciidoctor", "adoc"); - } - - } - - private static final class MarkdownTemplateFormat extends AbstractTemplateFormat { - - private MarkdownTemplateFormat() { - super("markdown", "md"); - } - - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateResourceResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateResourceResolver.java deleted file mode 100644 index 5e80193ce..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/TemplateResourceResolver.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -import org.springframework.core.io.Resource; - -/** - * A {@code TemplateResourceResolver} is responsible for resolving a name for a template - * into a {@link Resource} from which the template can be read. - * - * @author Andy Wilkinson - */ -public interface TemplateResourceResolver { - - /** - * Resolves a {@link Resource} for the template with the given {@code name}. - * @param name the name of the template - * @return the {@code Resource} from which the template can be read - */ - Resource resolveTemplateResource(String name); - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambda.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambda.java deleted file mode 100644 index 29d56199f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambda.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates.mustache; - -import java.io.IOException; -import java.io.Writer; - -import org.springframework.restdocs.mustache.Mustache.Lambda; -import org.springframework.restdocs.mustache.Template.Fragment; - -/** - * A {@link Lambda} that escapes {@code |} characters so that the do not break the table's - * formatting. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public final class AsciidoctorTableCellContentLambda implements Lambda { - - @Override - public void execute(Fragment fragment, Writer writer) throws IOException { - String output = fragment.execute(); - for (int i = 0; i < output.length(); i++) { - char current = output.charAt(i); - if (current == '|' && (i == 0 || output.charAt(i - 1) != '\\')) { - writer.append('\\'); - } - writer.append(current); - } - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/MustacheTemplate.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/MustacheTemplate.java deleted file mode 100644 index 3fc9e3a41..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/MustacheTemplate.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates.mustache; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.restdocs.templates.Template; - -/** - * An adapter that exposes a compiled Mustache - * template as a {@link Template}. - * - * @author Andy Wilkinson - */ -public class MustacheTemplate implements Template { - - private final org.springframework.restdocs.mustache.Template delegate; - - private final Map context; - - /** - * Creates a new {@code MustacheTemplate} that adapts the given {@code delegate}. - * @param delegate the delegate to adapt - */ - public MustacheTemplate(org.springframework.restdocs.mustache.Template delegate) { - this(delegate, Collections.emptyMap()); - } - - /** - * Creates a new {@code MustacheTemplate} that adapts the given {@code delegate}. - * During rendering, the given {@code context} and the context passed into - * {@link #render(Map)} will be combined and then passed to the delegate when it is - * {@link org.springframework.restdocs.mustache.Template#execute executed}. - * @param delegate the delegate to adapt - * @param context the context - */ - public MustacheTemplate(org.springframework.restdocs.mustache.Template delegate, Map context) { - this.delegate = delegate; - this.context = context; - } - - @Override - public String render(Map context) { - Map combinedContext = new HashMap<>(this.context); - combinedContext.putAll(context); - return this.delegate.execute(combinedContext); - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/MustacheTemplateEngine.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/MustacheTemplateEngine.java deleted file mode 100644 index 584c871a5..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/MustacheTemplateEngine.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2014-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates.mustache; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Map; - -import org.springframework.core.io.Resource; -import org.springframework.restdocs.mustache.Mustache; -import org.springframework.restdocs.mustache.Mustache.Compiler; -import org.springframework.restdocs.templates.Template; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateResourceResolver; - -/** - * A Mustache-based {@link TemplateEngine} - * implemented using JMustache. - *

- * Note that JMustache has been repackaged and embedded to prevent classpath conflicts. - * - * @author Andy Wilkinson - */ -public class MustacheTemplateEngine implements TemplateEngine { - - private final TemplateResourceResolver templateResourceResolver; - - private final Charset templateEncoding; - - private final Compiler compiler; - - private final Map context; - - /** - * Creates a new {@code MustacheTemplateEngine} that will use the given - * {@code templateResourceResolver} to resolve template paths. Templates will be read - * as UTF-8. - * @param templateResourceResolver the resolver to use - */ - public MustacheTemplateEngine(TemplateResourceResolver templateResourceResolver) { - this(templateResourceResolver, Mustache.compiler().escapeHTML(false)); - } - - /** - * Creates a new {@code MustacheTemplateEngine} that will use the given - * {@code templateResourceResolver} to resolve template paths, reading them using the - * given {@code templateEncoding}. - * @param templateResourceResolver the resolver to use - * @param templateEncoding the charset to use when reading the templates - * @since 2.0.5 - */ - public MustacheTemplateEngine(TemplateResourceResolver templateResourceResolver, Charset templateEncoding) { - this(templateResourceResolver, templateEncoding, Mustache.compiler().escapeHTML(false)); - } - - /** - * Creates a new {@code MustacheTemplateEngine} that will use the given - * {@code templateResourceResolver} to resolve templates and the given - * {@code compiler} to compile them. Templates will be read as UTF-8. - * @param templateResourceResolver the resolver to use - * @param compiler the compiler to use - */ - public MustacheTemplateEngine(TemplateResourceResolver templateResourceResolver, Compiler compiler) { - this(templateResourceResolver, compiler, Collections.emptyMap()); - } - - /** - * Creates a new {@code MustacheTemplateEngine} that will use the given - * {@code templateResourceResolver} to resolve templates and the given - * {@code compiler} to compile them. Templates will be read using the given - * {@code templateEncoding}. - * @param templateResourceResolver the resolver to use - * @param templateEncoding the charset to use when reading the templates - * @param compiler the compiler to use - * @since 2.0.5 - */ - public MustacheTemplateEngine(TemplateResourceResolver templateResourceResolver, Charset templateEncoding, - Compiler compiler) { - this(templateResourceResolver, templateEncoding, compiler, Collections.emptyMap()); - } - - /** - * Creates a new {@code MustacheTemplateEngine} that will use the given - * {@code templateResourceResolver} to resolve templates. Templates will be read as - * UTF-8. Once read, the given {@code compiler} will be used to compile them. Compiled - * templates will be created with the given {@code context}. - * @param templateResourceResolver the resolver to use - * @param compiler the compiler to use - * @param context the context to pass to compiled templates - * @see MustacheTemplate#MustacheTemplate(org.springframework.restdocs.mustache.Template, - * Map) - */ - public MustacheTemplateEngine(TemplateResourceResolver templateResourceResolver, Compiler compiler, - Map context) { - this(templateResourceResolver, StandardCharsets.UTF_8, compiler, context); - } - - /** - * Creates a new {@code MustacheTemplateEngine} that will use the given - * {@code templateResourceResolver} to resolve templates. Template will be read using - * the given {@code templateEncoding}. Once read, the given {@code compiler} will be - * used to compile them. Compiled templates will be created with the given - * {@code context}. - * @param templateResourceResolver the resolver to use - * @param templateEncoding the charset to use when reading the templates - * @param compiler the compiler to use - * @param context the context to pass to compiled templates - * @since 2.0.5 - * @see MustacheTemplate#MustacheTemplate(org.springframework.restdocs.mustache.Template, - * Map) - */ - public MustacheTemplateEngine(TemplateResourceResolver templateResourceResolver, Charset templateEncoding, - Compiler compiler, Map context) { - this.templateResourceResolver = templateResourceResolver; - this.templateEncoding = templateEncoding; - this.compiler = compiler; - this.context = context; - } - - @Override - public Template compileTemplate(String name) throws IOException { - Resource templateResource = this.templateResourceResolver.resolveTemplateResource(name); - return new MustacheTemplate( - this.compiler.compile(new InputStreamReader(templateResource.getInputStream(), this.templateEncoding)), - this.context); - } - - /** - * Returns the {@link Compiler} used to compile Mustache templates. - * @return the compiler - */ - protected final Compiler getCompiler() { - return this.compiler; - } - - /** - * Returns the {@link TemplateResourceResolver} used to resolve the template resources - * prior to compilation. - * @return the resolver - */ - protected final TemplateResourceResolver getTemplateResourceResolver() { - return this.templateResourceResolver; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/package-info.java deleted file mode 100644 index b9f21708f..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/mustache/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * JMustache-based implementation of the template API. - */ -package org.springframework.restdocs.templates.mustache; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/package-info.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/package-info.java deleted file mode 100644 index ba58f2b76..000000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/templates/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Template API used to render documentation snippets. - */ -package org.springframework.restdocs.templates; diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties deleted file mode 100644 index 699b900e2..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties +++ /dev/null @@ -1,32 +0,0 @@ -jakarta.validation.constraints.AssertFalse.description=Must be false -jakarta.validation.constraints.AssertTrue.description=Must be true -jakarta.validation.constraints.DecimalMax.description=Must be at most ${value} -jakarta.validation.constraints.DecimalMin.description=Must be at least ${value} -jakarta.validation.constraints.Digits.description=Must have at most ${integer} integral digits and ${fraction} fractional digits -jakarta.validation.constraints.Email.description=Must be a well-formed email address -jakarta.validation.constraints.Future.description=Must be in the future -jakarta.validation.constraints.FutureOrPresent.description=Must be in the future or the present -jakarta.validation.constraints.Max.description=Must be at most ${value} -jakarta.validation.constraints.Min.description=Must be at least ${value} -jakarta.validation.constraints.Negative.description=Must be negative -jakarta.validation.constraints.NegativeOrZero.description=Must be negative or zero -jakarta.validation.constraints.NotBlank.description=Must not be blank -jakarta.validation.constraints.NotEmpty.description=Must not be empty -jakarta.validation.constraints.NotNull.description=Must not be null -jakarta.validation.constraints.Null.description=Must be null -jakarta.validation.constraints.Past.description=Must be in the past -jakarta.validation.constraints.PastOrPresent.description=Must be in the past or the present -jakarta.validation.constraints.Pattern.description=Must match the regular expression `${regexp}` -jakarta.validation.constraints.Positive.description=Must be positive -jakarta.validation.constraints.PositiveOrZero.description=Must be positive or zero -jakarta.validation.constraints.Size.description=Size must be between ${min} and ${max} inclusive -org.hibernate.validator.constraints.CodePointLength.description=Code point length must be between ${min} and ${max} inclusive -org.hibernate.validator.constraints.CreditCardNumber.description=Must be a well-formed credit card number -org.hibernate.validator.constraints.Currency.description=Must be in an accepted currency unit (${value}) -org.hibernate.validator.constraints.EAN.description=Must be a well-formed ${type} number -org.hibernate.validator.constraints.Length.description=Length must be between ${min} and ${max} inclusive -org.hibernate.validator.constraints.LuhnCheck.description=Must pass the Luhn Modulo 10 checksum algorithm -org.hibernate.validator.constraints.Mod10Check.description=Must pass the Mod10 checksum algorithm -org.hibernate.validator.constraints.Mod11Check.description=Must pass the Mod11 checksum algorithm -org.hibernate.validator.constraints.Range.description=Must be at least ${min} and at most ${max} -org.hibernate.validator.constraints.URL.description=Must be a well-formed URL \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-curl-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-curl-request.snippet deleted file mode 100644 index 81fe65eeb..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-curl-request.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source,bash] ----- -$ curl {{url}} {{options}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-form-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-form-parameters.snippet deleted file mode 100644 index 9e6f6888a..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-form-parameters.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Parameter|Description - -{{#parameters}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-request.snippet deleted file mode 100644 index 9893e2a03..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-request.snippet +++ /dev/null @@ -1,8 +0,0 @@ -[source,http,options="nowrap"] ----- -{{method}} {{path}} HTTP/1.1 -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{requestBody}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-response.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-response.snippet deleted file mode 100644 index 90a25e851..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-http-response.snippet +++ /dev/null @@ -1,8 +0,0 @@ -[source,http,options="nowrap"] ----- -HTTP/1.1 {{statusCode}} {{statusReason}} -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{responseBody}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-httpie-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-httpie-request.snippet deleted file mode 100644 index 1b690a5f5..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-httpie-request.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source,bash] ----- -$ {{echoContent}}http {{options}} {{url}}{{requestItems}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-links.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-links.snippet deleted file mode 100644 index fda4f8376..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-links.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Relation|Description - -{{#links}} -|{{#tableCellContent}}`+{{rel}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/links}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-path-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-path-parameters.snippet deleted file mode 100644 index 6976b7b28..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-path-parameters.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.+{{path}}+ -|=== -|Parameter|Description - -{{#parameters}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-query-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-query-parameters.snippet deleted file mode 100644 index 9e6f6888a..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-query-parameters.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Parameter|Description - -{{#parameters}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-body.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-body.snippet deleted file mode 100644 index 00da2d085..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-body.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source{{#language}},{{language}}{{/language}},options="nowrap"] ----- -{{body}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-cookies.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-cookies.snippet deleted file mode 100644 index 0c5315051..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-cookies.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Name|Description - -{{#cookies}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/cookies}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-fields.snippet deleted file mode 100644 index 0d8f18e93..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-fields.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Path|Type|Description - -{{#fields}} -|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} -|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-headers.snippet deleted file mode 100644 index 5a8593332..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-headers.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Name|Description - -{{#headers}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/headers}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parameters.snippet deleted file mode 100644 index 9e6f6888a..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parameters.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Parameter|Description - -{{#parameters}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-part-body.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-part-body.snippet deleted file mode 100644 index 00da2d085..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-part-body.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source{{#language}},{{language}}{{/language}},options="nowrap"] ----- -{{body}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-part-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-part-fields.snippet deleted file mode 100644 index 0d8f18e93..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-part-fields.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Path|Type|Description - -{{#fields}} -|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} -|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parts.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parts.snippet deleted file mode 100644 index 23a23436c..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-request-parts.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Part|Description - -{{#requestParts}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/requestParts}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-body.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-body.snippet deleted file mode 100644 index 00da2d085..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-body.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source{{#language}},{{language}}{{/language}},options="nowrap"] ----- -{{body}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-cookies.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-cookies.snippet deleted file mode 100644 index 0c5315051..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-cookies.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Name|Description - -{{#cookies}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/cookies}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-fields.snippet deleted file mode 100644 index 0d8f18e93..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-fields.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Path|Type|Description - -{{#fields}} -|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}} -|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-headers.snippet deleted file mode 100644 index 5a8593332..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/asciidoctor/default-response-headers.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Name|Description - -{{#headers}} -|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} -|{{#tableCellContent}}{{description}}{{/tableCellContent}} - -{{/headers}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-curl-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-curl-request.snippet deleted file mode 100644 index 085894270..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-curl-request.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```bash -$ curl {{url}} {{options}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-form-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-form-parameters.snippet deleted file mode 100644 index 681daaa8e..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-form-parameters.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Description ---------- | ----------- -{{#parameters}} -`{{name}}` | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-request.snippet deleted file mode 100644 index 9613f5fc7..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-request.snippet +++ /dev/null @@ -1,7 +0,0 @@ -```http -{{method}} {{path}} HTTP/1.1 -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{requestBody}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-response.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-response.snippet deleted file mode 100644 index 1b01aa5b7..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-http-response.snippet +++ /dev/null @@ -1,7 +0,0 @@ -```http -HTTP/1.1 {{statusCode}} {{statusReason}} -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{responseBody}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-httpie-request.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-httpie-request.snippet deleted file mode 100644 index 65d78f740..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-httpie-request.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```bash -$ {{echoContent}}http {{options}} {{url}}{{requestItems}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-links.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-links.snippet deleted file mode 100644 index 8c690d0f1..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-links.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Relation | Description --------- | ----------- -{{#links}} -`{{rel}}` | {{description}} -{{/links}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-path-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-path-parameters.snippet deleted file mode 100644 index 76dfc572e..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-path-parameters.snippet +++ /dev/null @@ -1,7 +0,0 @@ -`{{path}}` - -Parameter | Description ---------- | ----------- -{{#parameters}} -`{{name}}` | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-query-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-query-parameters.snippet deleted file mode 100644 index 681daaa8e..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-query-parameters.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Description ---------- | ----------- -{{#parameters}} -`{{name}}` | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-body.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-body.snippet deleted file mode 100644 index 6abf3f7e8..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-body.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```{{#language}}{{language}}{{/language}} -{{body}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-cookies.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-cookies.snippet deleted file mode 100644 index dbc046b82..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-cookies.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description ----- | ----------- -{{#cookies}} -`{{name}}` | {{description}} -{{/cookies}} diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-fields.snippet deleted file mode 100644 index 27a4e4379..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-fields.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Path | Type | Description ----- | ---- | ----------- -{{#fields}} -`{{path}}` | `{{type}}` | {{description}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-headers.snippet deleted file mode 100644 index 7bbe694df..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-headers.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description ----- | ----------- -{{#headers}} -`{{name}}` | {{description}} -{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parameters.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parameters.snippet deleted file mode 100644 index 681daaa8e..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parameters.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Description ---------- | ----------- -{{#parameters}} -`{{name}}` | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-part-body.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-part-body.snippet deleted file mode 100644 index 6abf3f7e8..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-part-body.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```{{#language}}{{language}}{{/language}} -{{body}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-part-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-part-fields.snippet deleted file mode 100644 index 27a4e4379..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-part-fields.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Path | Type | Description ----- | ---- | ----------- -{{#fields}} -`{{path}}` | `{{type}}` | {{description}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parts.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parts.snippet deleted file mode 100644 index b313f2d3e..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-request-parts.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Part | Description ----- | ----------- -{{#requestParts}} -`{{name}}` | {{description}} -{{/requestParts}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-body.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-body.snippet deleted file mode 100644 index 6abf3f7e8..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-body.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```{{#language}}{{language}}{{/language}} -{{body}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-cookies.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-cookies.snippet deleted file mode 100644 index dbc046b82..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-cookies.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description ----- | ----------- -{{#cookies}} -`{{name}}` | {{description}} -{{/cookies}} diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-fields.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-fields.snippet deleted file mode 100644 index 27a4e4379..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-fields.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Path | Type | Description ----- | ---- | ----------- -{{#fields}} -`{{path}}` | `{{type}}` | {{description}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-headers.snippet b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-headers.snippet deleted file mode 100644 index 7bbe694df..000000000 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/templates/markdown/default-response-headers.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description ----- | ----------- -{{#headers}} -`{{name}}` | {{description}} -{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java deleted file mode 100644 index bc0a5d09e..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/AbstractSnippetTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import org.springframework.core.io.FileSystemResource; -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.SnippetConditions; -import org.springframework.restdocs.testfixtures.SnippetConditions.CodeBlockCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpRequestCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.TableCondition; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * Abstract base class for testing snippet generation. - * - * @author Andy Wilkinson - */ -public abstract class AbstractSnippetTests { - - protected final TemplateFormat templateFormat = TemplateFormats.asciidoctor(); - - protected AssertableSnippets snippets; - - public CodeBlockCondition codeBlock(String language) { - return this.codeBlock(language, null); - } - - public CodeBlockCondition codeBlock(String language, String options) { - return SnippetConditions.codeBlock(this.templateFormat, language, options); - } - - public TableCondition tableWithHeader(String... headers) { - return SnippetConditions.tableWithHeader(this.templateFormat, headers); - } - - public TableCondition tableWithTitleAndHeader(String title, String... headers) { - return SnippetConditions.tableWithTitleAndHeader(this.templateFormat, title, headers); - } - - public HttpRequestCondition httpRequest(RequestMethod method, String uri) { - return SnippetConditions.httpRequest(this.templateFormat, method, uri); - } - - public HttpResponseCondition httpResponse(HttpStatus responseStatus) { - return SnippetConditions.httpResponse(this.templateFormat, responseStatus); - } - - public HttpResponseCondition httpResponse(int responseStatusCode) { - return SnippetConditions.httpResponse(this.templateFormat, responseStatusCode, ""); - } - - protected FileSystemResource snippetResource(String name) { - return new FileSystemResource( - "src/test/resources/custom-snippet-templates/" + this.templateFormat.getId() + "/" + name + ".snippet"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java deleted file mode 100644 index 3b41392a1..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs; - -import java.io.IOException; -import java.net.URI; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mockito; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.RequestConverter; -import org.springframework.restdocs.operation.ResponseConverter; -import org.springframework.restdocs.operation.preprocess.OperationPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.operation.preprocess.Preprocessors; -import org.springframework.restdocs.snippet.Snippet; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -/** - * Tests for {@link RestDocumentationGenerator}. - * - * @author Andy Wilkinson - * @author Filip Hrisafov - */ -class RestDocumentationGeneratorTests { - - @SuppressWarnings("unchecked") - private final RequestConverter requestConverter = mock(RequestConverter.class); - - @SuppressWarnings("unchecked") - private final ResponseConverter responseConverter = mock(ResponseConverter.class); - - private final Object request = new Object(); - - private final Object response = new Object(); - - private final OperationRequest operationRequest = new OperationRequestFactory() - .create(URI.create("http://localhost:8080"), null, null, new HttpHeaders(), null, null); - - private final OperationResponse operationResponse = new OperationResponseFactory().create(HttpStatus.OK, null, - null); - - private final Snippet snippet = mock(Snippet.class); - - private final OperationPreprocessor requestPreprocessor = mock(OperationPreprocessor.class); - - private final OperationPreprocessor responsePreprocessor = mock(OperationPreprocessor.class); - - @Test - void basicHandling() throws IOException { - given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); - given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); - HashMap configuration = new HashMap<>(); - new RestDocumentationGenerator<>("id", this.requestConverter, this.responseConverter, this.snippet) - .handle(this.request, this.response, configuration); - verifySnippetInvocation(this.snippet, configuration); - } - - @Test - void defaultSnippetsAreCalled() throws IOException { - given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); - given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); - HashMap configuration = new HashMap<>(); - Snippet defaultSnippet1 = mock(Snippet.class); - Snippet defaultSnippet2 = mock(Snippet.class); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS, - Arrays.asList(defaultSnippet1, defaultSnippet2)); - new RestDocumentationGenerator<>("id", this.requestConverter, this.responseConverter, this.snippet) - .handle(this.request, this.response, configuration); - InOrder inOrder = Mockito.inOrder(defaultSnippet1, defaultSnippet2, this.snippet); - verifySnippetInvocation(inOrder, defaultSnippet1, configuration); - verifySnippetInvocation(inOrder, defaultSnippet2, configuration); - verifySnippetInvocation(inOrder, this.snippet, configuration); - } - - @Test - void defaultOperationRequestPreprocessorsAreCalled() throws IOException { - given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); - given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); - HashMap configuration = new HashMap<>(); - OperationPreprocessor defaultPreprocessor1 = mock(OperationPreprocessor.class); - OperationPreprocessor defaultPreprocessor2 = mock(OperationPreprocessor.class); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR, - Preprocessors.preprocessRequest(defaultPreprocessor1, defaultPreprocessor2)); - OperationRequest first = createRequest(); - OperationRequest second = createRequest(); - OperationRequest third = createRequest(); - given(this.requestPreprocessor.preprocess(this.operationRequest)).willReturn(first); - given(defaultPreprocessor1.preprocess(first)).willReturn(second); - given(defaultPreprocessor2.preprocess(second)).willReturn(third); - new RestDocumentationGenerator<>("id", this.requestConverter, this.responseConverter, - Preprocessors.preprocessRequest(this.requestPreprocessor), this.snippet) - .handle(this.request, this.response, configuration); - verifySnippetInvocation(this.snippet, third, this.operationResponse, configuration, 1); - } - - @Test - void defaultOperationResponsePreprocessorsAreCalled() throws IOException { - given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); - given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); - HashMap configuration = new HashMap<>(); - OperationPreprocessor defaultPreprocessor1 = mock(OperationPreprocessor.class); - OperationPreprocessor defaultPreprocessor2 = mock(OperationPreprocessor.class); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR, - Preprocessors.preprocessResponse(defaultPreprocessor1, defaultPreprocessor2)); - OperationResponse first = createResponse(); - OperationResponse second = createResponse(); - OperationResponse third = new OperationResponseFactory().createFrom(this.operationResponse, new HttpHeaders()); - given(this.responsePreprocessor.preprocess(this.operationResponse)).willReturn(first); - given(defaultPreprocessor1.preprocess(first)).willReturn(second); - given(defaultPreprocessor2.preprocess(second)).willReturn(third); - new RestDocumentationGenerator<>("id", this.requestConverter, this.responseConverter, - Preprocessors.preprocessResponse(this.responsePreprocessor), this.snippet) - .handle(this.request, this.response, configuration); - verifySnippetInvocation(this.snippet, this.operationRequest, third, configuration, 1); - } - - @Test - void newGeneratorOnlyCallsItsSnippets() throws IOException { - OperationRequestPreprocessor requestPreprocessor = mock(OperationRequestPreprocessor.class); - OperationResponsePreprocessor responsePreprocessor = mock(OperationResponsePreprocessor.class); - given(this.requestConverter.convert(this.request)).willReturn(this.operationRequest); - given(this.responseConverter.convert(this.response)).willReturn(this.operationResponse); - given(requestPreprocessor.preprocess(this.operationRequest)).willReturn(this.operationRequest); - given(responsePreprocessor.preprocess(this.operationResponse)).willReturn(this.operationResponse); - Snippet additionalSnippet1 = mock(Snippet.class); - Snippet additionalSnippet2 = mock(Snippet.class); - RestDocumentationGenerator generator = new RestDocumentationGenerator<>("id", - this.requestConverter, this.responseConverter, requestPreprocessor, responsePreprocessor, this.snippet); - HashMap configuration = new HashMap<>(); - generator.withSnippets(additionalSnippet1, additionalSnippet2) - .handle(this.request, this.response, configuration); - verifyNoMoreInteractions(this.snippet); - verifySnippetInvocation(additionalSnippet1, configuration); - verifySnippetInvocation(additionalSnippet2, configuration); - } - - private void verifySnippetInvocation(Snippet snippet, Map attributes) throws IOException { - ArgumentCaptor operation = ArgumentCaptor.forClass(Operation.class); - verify(snippet).document(operation.capture()); - assertThat(this.operationRequest).isEqualTo(operation.getValue().getRequest()); - assertThat(this.operationResponse).isEqualTo(operation.getValue().getResponse()); - assertThat(attributes).isEqualTo(operation.getValue().getAttributes()); - } - - private void verifySnippetInvocation(Snippet snippet, OperationRequest operationRequest, - OperationResponse operationResponse, Map attributes, int times) throws IOException { - ArgumentCaptor operation = ArgumentCaptor.forClass(Operation.class); - verify(snippet, Mockito.times(times)).document(operation.capture()); - assertThat(operationRequest).isEqualTo(operation.getValue().getRequest()); - assertThat(operationResponse).isEqualTo(operation.getValue().getResponse()); - } - - private void verifySnippetInvocation(InOrder inOrder, Snippet snippet, Map attributes) - throws IOException { - ArgumentCaptor operation = ArgumentCaptor.forClass(Operation.class); - inOrder.verify(snippet).document(operation.capture()); - assertThat(this.operationRequest).isEqualTo(operation.getValue().getRequest()); - assertThat(this.operationResponse).isEqualTo(operation.getValue().getResponse()); - assertThat(attributes).isEqualTo(operation.getValue().getAttributes()); - } - - private static OperationRequest createRequest() { - return new OperationRequestFactory().create(URI.create("http://localhost:8080"), null, null, new HttpHeaders(), - null, null); - } - - private static OperationResponse createResponse() { - return new OperationResponseFactory().create(HttpStatus.OK, null, null); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java deleted file mode 100644 index c69c879bb..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/ConcatenatingCommandFormatterTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link CommandFormatter}. - * - * @author Tomasz Kopczynski - * @author Andy Wilkinson - */ -class ConcatenatingCommandFormatterTests { - - private CommandFormatter singleLineFormat = new ConcatenatingCommandFormatter(" "); - - @Test - void formattingAnEmptyListProducesAnEmptyString() { - assertThat(this.singleLineFormat.format(Collections.emptyList())).isEqualTo(""); - } - - @Test - void formattingNullProducesAnEmptyString() { - assertThat(this.singleLineFormat.format(null)).isEqualTo(""); - } - - @Test - void formattingASingleElement() { - assertThat(this.singleLineFormat.format(Collections.singletonList("alpha"))).isEqualTo(" alpha"); - } - - @Test - void formattingMultipleElements() { - assertThat(this.singleLineFormat.format(Arrays.asList("alpha", "bravo"))).isEqualTo(" alpha bravo"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java deleted file mode 100644 index e0136a1dd..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.io.IOException; -import java.util.Base64; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link CurlRequestSnippet}. - * - * @author Andy Wilkinson - * @author Yann Le Guern - * @author Dmitriy Mayboroda - * @author Jonathan Pearlin - * @author Paul-Christian Volkmer - * @author Tomasz Kopczynski - */ -class CurlRequestSnippetTests { - - private CommandFormatter commandFormatter = CliDocumentation.singleLineFormat(); - - @RenderedSnippetTest - void getRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").build()); - assertThat(snippets.curlRequest()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguage("bash").content("$ curl 'http://localhost/foo' -i -X GET")); - } - - @RenderedSnippetTest - void nonGetRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("POST").build()); - assertThat(snippets.curlRequest()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguage("bash").content("$ curl 'http://localhost/foo' -i -X POST")); - } - - @RenderedSnippetTest - void requestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").content("content").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X GET -d 'content'")); - } - - @RenderedSnippetTest - void getRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param=value").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo?param=value' -i -X GET")); - } - - @RenderedSnippetTest - void getRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param").build()); - assertThat(snippets.curlRequest()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguage("bash").content("$ curl 'http://localhost/foo?param' -i -X GET")); - } - - @RenderedSnippetTest - void postRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param=value").method("POST").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo?param=value' -i -X POST")); - } - - @RenderedSnippetTest - void postRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param").method("POST").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo?param' -i -X POST")); - } - - @RenderedSnippetTest - void postRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("POST").content("k1=v1").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); - } - - @RenderedSnippetTest - void postRequestWithOneParameterAndExplicitContentType(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .method("POST") - .content("k1=v1") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'")); - } - - @RenderedSnippetTest - void postRequestWithOneParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("POST").content("k1=").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1='")); - } - - @RenderedSnippetTest - void postRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("POST") - .content("k1=v1&k1=v1-bis&k2=v2") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X POST" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); - } - - @RenderedSnippetTest - void postRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("POST").content("k1=a%26b").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'")); - } - - @RenderedSnippetTest - void postRequestWithJsonData(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .content("{\"a\":\"alpha\"}") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content( - "$ curl 'http://localhost/foo' -i -X POST -H 'Content-Type: application/json' -d '{\"a\":\"alpha\"}'")); - } - - @RenderedSnippetTest - void putRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("PUT").content("k1=v1").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'")); - } - - @RenderedSnippetTest - void putRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("PUT") - .content("k1=v1&k1=v1-bis&k2=v2") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X PUT" + " -d 'k1=v1&k1=v1-bis&k2=v2'")); - } - - @RenderedSnippetTest - void putRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("PUT").content("k1=a%26b").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'")); - } - - @RenderedSnippetTest - void requestWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X GET" + " -H 'Content-Type: application/json' -H 'a: alpha'")); - } - - @RenderedSnippetTest - void requestWithHeadersMultiline(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(CliDocumentation.multiLineFormat()) - .document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content(String.format("$ curl 'http://localhost/foo' -i -X GET \\%n" - + " -H 'Content-Type: application/json' \\%n" + " -H 'a: alpha'"))); - } - - @RenderedSnippetTest - void requestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .cookie("name1", "value1") - .cookie("name2", "value2") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X GET" + " --cookie 'name1=value1;name2=value2'")); - } - - @RenderedSnippetTest - void multipartPostWithNoSubmittedFileName(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("metadata", "{\"description\": \"foo\"}".getBytes()) - .build()); - String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H " - + "'Content-Type: multipart/form-data' -F " + "'metadata={\"description\": \"foo\"}'"; - assertThat(snippets.curlRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPostWithContentType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", new byte[0]) - .header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE) - .submittedFileName("documents/images/example.png") - .build()); - String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H " - + "'Content-Type: multipart/form-data' -F " + "'image=@documents/images/example.png;type=image/png'"; - assertThat(snippets.curlRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", new byte[0]) - .submittedFileName("documents/images/example.png") - .build()); - String expectedContent = "$ curl 'http://localhost/upload' -i -X POST -H " - + "'Content-Type: multipart/form-data' -F " + "'image=@documents/images/example.png'"; - assertThat(snippets.curlRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); - } - - @RenderedSnippetTest - void basicAuthCredentialsAreSuppliedUsingUserOption(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString("user:secret".getBytes())) - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -u 'user:secret' -X GET")); - } - - @RenderedSnippetTest - void customAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.HOST, "api.example.com") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo' -i -X GET -H 'Host: api.example.com'" - + " -H 'Content-Type: application/json' -H 'a: alpha'")); - } - - @RenderedSnippetTest - void deleteWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new CurlRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); - assertThat(snippets.curlRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ curl 'http://localhost/foo?a=alpha&b=bravo' -i " + "-X DELETE")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java deleted file mode 100644 index 89500ef06..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cli; - -import java.io.IOException; -import java.util.Base64; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link HttpieRequestSnippet}. - * - * @author Andy Wilkinson - * @author Yann Le Guern - * @author Dmitriy Mayboroda - * @author Jonathan Pearlin - * @author Paul-Christian Volkmer - * @author Raman Gupta - * @author Tomasz Kopczynski - */ -class HttpieRequestSnippetTests { - - private CommandFormatter commandFormatter = CliDocumentation.singleLineFormat(); - - @RenderedSnippetTest - void getRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").build()); - assertThat(snippets.httpieRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content("$ http GET 'http://localhost/foo'")); - } - - @RenderedSnippetTest - void nonGetRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").method("POST").build()); - assertThat(snippets.httpieRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content("$ http POST 'http://localhost/foo'")); - } - - @RenderedSnippetTest - void requestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo").content("content").build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ echo 'content' | http GET 'http://localhost/foo'")); - } - - @RenderedSnippetTest - void getRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param=value").build()); - assertThat(snippets.httpieRequest()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguage("bash").content("$ http GET 'http://localhost/foo?param=value'")); - } - - @RenderedSnippetTest - void getRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param").build()); - assertThat(snippets.httpieRequest()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguage("bash").content("$ http GET 'http://localhost/foo?param'")); - } - - @RenderedSnippetTest - void postRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param=value").method("POST").build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http POST 'http://localhost/foo?param=value'")); - } - - @RenderedSnippetTest - void postRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?param").method("POST").build()); - assertThat(snippets.httpieRequest()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguage("bash").content("$ http POST 'http://localhost/foo?param'")); - } - - @RenderedSnippetTest - void postRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1=v1") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form POST 'http://localhost/foo' 'k1=v1'")); - } - - @RenderedSnippetTest - void postRequestWithOneParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form POST 'http://localhost/foo' 'k1='")); - } - - @RenderedSnippetTest - void postRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1=v1&k1=v1-bis&k2=v2") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form POST 'http://localhost/foo' 'k1=v1' 'k1=v1-bis' 'k2=v2'")); - } - - @RenderedSnippetTest - void postRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1=a%26b") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form POST 'http://localhost/foo' 'k1=a&b'")); - } - - @RenderedSnippetTest - void putRequestWithOneParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("PUT") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1=v1") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form PUT 'http://localhost/foo' 'k1=v1'")); - } - - @RenderedSnippetTest - void putRequestWithMultipleParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("PUT") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1=v1&k1=v1-bis&k2=v2") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form PUT 'http://localhost/foo'" + " 'k1=v1' 'k1=v1-bis' 'k2=v2'")); - } - - @RenderedSnippetTest - void putRequestWithUrlEncodedParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .method("PUT") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .content("k1=a%26b") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --form PUT 'http://localhost/foo' 'k1=a&b'")); - } - - @RenderedSnippetTest - void requestWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content(("$ http GET 'http://localhost/foo'" + " 'Content-Type:application/json' 'a:alpha'"))); - } - - @RenderedSnippetTest - void requestWithHeadersMultiline(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(CliDocumentation.multiLineFormat()) - .document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content(String.format("$ http GET 'http://localhost/foo' \\%n" - + " 'Content-Type:application/json' \\%n 'a:alpha'"))); - } - - @RenderedSnippetTest - void requestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .cookie("name1", "value1") - .cookie("name2", "value2") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content(("$ http GET 'http://localhost/foo'" + " 'Cookie:name1=value1' 'Cookie:name2=value2'"))); - } - - @RenderedSnippetTest - void multipartPostWithNoSubmittedFileName(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("metadata", "{\"description\": \"foo\"}".getBytes()) - .build()); - String expectedContent = "$ http --multipart POST 'http://localhost/upload'" - + " 'metadata'='{\"description\": \"foo\"}'"; - assertThat(snippets.httpieRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPostWithContentType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", new byte[0]) - .header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE) - .submittedFileName("documents/images/example.png") - .build()); - // httpie does not yet support manually set content type by part - String expectedContent = "$ http --multipart POST 'http://localhost/upload'" - + " 'image'@'documents/images/example.png'"; - assertThat(snippets.httpieRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", new byte[0]) - .submittedFileName("documents/images/example.png") - .build()); - String expectedContent = "$ http --multipart POST 'http://localhost/upload'" - + " 'image'@'documents/images/example.png'"; - assertThat(snippets.httpieRequest()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash").content(expectedContent)); - } - - @RenderedSnippetTest - void basicAuthCredentialsAreSuppliedUsingAuthOption(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString("user:secret".getBytes())) - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http --auth 'user:secret' GET 'http://localhost/foo'")); - } - - @RenderedSnippetTest - void customAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter).document(operationBuilder.request("http://localhost/foo") - .header(HttpHeaders.HOST, "api.example.com") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http GET 'http://localhost/foo' 'Host:api.example.com'" - + " 'Content-Type:application/json' 'a:alpha'")); - } - - @RenderedSnippetTest - void deleteWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpieRequestSnippet(this.commandFormatter) - .document(operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); - assertThat(snippets.httpieRequest()).isCodeBlock((codeBlock) -> codeBlock.withLanguage("bash") - .content("$ http DELETE 'http://localhost/foo?a=alpha&b=bravo'")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java deleted file mode 100644 index aa6c68141..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/config/RestDocumentationConfigurerTests.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.config; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.ManualRestDocumentation; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.cli.CliDocumentation; -import org.springframework.restdocs.cli.CurlRequestSnippet; -import org.springframework.restdocs.cli.HttpieRequestSnippet; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.http.HttpRequestSnippet; -import org.springframework.restdocs.http.HttpResponseSnippet; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.operation.preprocess.Preprocessors; -import org.springframework.restdocs.payload.RequestBodySnippet; -import org.springframework.restdocs.payload.ResponseBodySnippet; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.restdocs.snippet.StandardWriterResolver; -import org.springframework.restdocs.snippet.WriterResolver; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.templates.mustache.AsciidoctorTableCellContentLambda; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.test.util.ReflectionTestUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link RestDocumentationConfigurer}. - * - * @author Andy Wilkinson - * @author Filip Hrisafov - */ -class RestDocumentationConfigurerTests { - - private final TestRestDocumentationConfigurer configurer = new TestRestDocumentationConfigurer(); - - @SuppressWarnings("unchecked") - @Test - void defaultConfiguration() { - Map configuration = new HashMap<>(); - this.configurer.apply(configuration, createContext()); - assertThat(configuration).containsKey(TemplateEngine.class.getName()); - assertThat(configuration.get(TemplateEngine.class.getName())).isInstanceOf(MustacheTemplateEngine.class); - assertThat(configuration.get(TemplateEngine.class.getName())).hasFieldOrPropertyWithValue("templateEncoding", - StandardCharsets.UTF_8); - assertThat(configuration).containsKey(WriterResolver.class.getName()); - assertThat(configuration.get(WriterResolver.class.getName())).isInstanceOf(StandardWriterResolver.class); - assertThat(configuration).containsKey(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS)) - .isInstanceOf(List.class); - List defaultSnippets = (List) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - assertThat(defaultSnippets).extracting("class") - .containsExactlyInAnyOrder(CurlRequestSnippet.class, HttpieRequestSnippet.class, HttpRequestSnippet.class, - HttpResponseSnippet.class, RequestBodySnippet.class, ResponseBodySnippet.class); - assertThat(configuration).containsKey(SnippetConfiguration.class.getName()); - assertThat(configuration.get(SnippetConfiguration.class.getName())).isInstanceOf(SnippetConfiguration.class); - SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration - .get(SnippetConfiguration.class.getName()); - assertThat(snippetConfiguration.getEncoding()).isEqualTo("UTF-8"); - assertThat(snippetConfiguration.getTemplateFormat().getId()).isEqualTo(TemplateFormats.asciidoctor().getId()); - OperationRequestPreprocessor defaultOperationRequestPreprocessor = (OperationRequestPreprocessor) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR); - assertThat(defaultOperationRequestPreprocessor).isNull(); - - OperationResponsePreprocessor defaultOperationResponsePreprocessor = (OperationResponsePreprocessor) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR); - assertThat(defaultOperationResponsePreprocessor).isNull(); - } - - @Test - void customTemplateEngine() { - Map configuration = new HashMap<>(); - TemplateEngine templateEngine = mock(TemplateEngine.class); - this.configurer.templateEngine(templateEngine).apply(configuration, createContext()); - assertThat(configuration).containsEntry(TemplateEngine.class.getName(), templateEngine); - } - - @Test - void customWriterResolver() { - Map configuration = new HashMap<>(); - WriterResolver writerResolver = mock(WriterResolver.class); - this.configurer.writerResolver(writerResolver).apply(configuration, createContext()); - assertThat(configuration).containsEntry(WriterResolver.class.getName(), writerResolver); - } - - @Test - void customDefaultSnippets() { - Map configuration = new HashMap<>(); - this.configurer.snippets().withDefaults(CliDocumentation.curlRequest()).apply(configuration, createContext()); - assertThat(configuration).containsKey(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS)) - .isInstanceOf(List.class); - @SuppressWarnings("unchecked") - List defaultSnippets = (List) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - assertThat(defaultSnippets).hasSize(1); - assertThat(defaultSnippets).hasOnlyElementsOfType(CurlRequestSnippet.class); - } - - @SuppressWarnings("unchecked") - @Test - void additionalDefaultSnippets() { - Map configuration = new HashMap<>(); - Snippet snippet = mock(Snippet.class); - this.configurer.snippets().withAdditionalDefaults(snippet).apply(configuration, createContext()); - assertThat(configuration).containsKey(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS)) - .isInstanceOf(List.class); - List defaultSnippets = (List) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - assertThat(defaultSnippets).extracting("class") - .containsExactlyInAnyOrder(CurlRequestSnippet.class, HttpieRequestSnippet.class, HttpRequestSnippet.class, - HttpResponseSnippet.class, RequestBodySnippet.class, ResponseBodySnippet.class, snippet.getClass()); - } - - @Test - void customSnippetEncoding() { - Map configuration = new HashMap<>(); - this.configurer.snippets().withEncoding("ISO-8859-1"); - this.configurer.apply(configuration, createContext()); - assertThat(configuration).containsKey(SnippetConfiguration.class.getName()); - assertThat(configuration.get(SnippetConfiguration.class.getName())).isInstanceOf(SnippetConfiguration.class); - SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration - .get(SnippetConfiguration.class.getName()); - assertThat(snippetConfiguration.getEncoding()).isEqualTo(StandardCharsets.ISO_8859_1.displayName()); - assertThat(configuration.get(TemplateEngine.class.getName())).hasFieldOrPropertyWithValue("templateEncoding", - StandardCharsets.ISO_8859_1); - } - - @Test - void customTemplateFormat() { - Map configuration = new HashMap<>(); - this.configurer.snippets().withTemplateFormat(TemplateFormats.markdown()).apply(configuration, createContext()); - assertThat(configuration).containsKey(SnippetConfiguration.class.getName()); - assertThat(configuration.get(SnippetConfiguration.class.getName())).isInstanceOf(SnippetConfiguration.class); - SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration - .get(SnippetConfiguration.class.getName()); - assertThat(snippetConfiguration.getTemplateFormat().getId()).isEqualTo(TemplateFormats.markdown().getId()); - } - - @SuppressWarnings("unchecked") - @Test - void asciidoctorTableCellContentLambaIsInstalledWhenUsingAsciidoctorTemplateFormat() { - Map configuration = new HashMap<>(); - this.configurer.apply(configuration, createContext()); - TemplateEngine templateEngine = (TemplateEngine) configuration.get(TemplateEngine.class.getName()); - MustacheTemplateEngine mustacheTemplateEngine = (MustacheTemplateEngine) templateEngine; - Map templateContext = (Map) ReflectionTestUtils.getField(mustacheTemplateEngine, - "context"); - assertThat(templateContext).containsKey("tableCellContent"); - assertThat(templateContext.get("tableCellContent")).isInstanceOf(AsciidoctorTableCellContentLambda.class); - } - - @SuppressWarnings("unchecked") - @Test - void asciidoctorTableCellContentLambaIsNotInstalledWhenUsingNonAsciidoctorTemplateFormat() { - Map configuration = new HashMap<>(); - this.configurer.snippetConfigurer.withTemplateFormat(TemplateFormats.markdown()); - this.configurer.apply(configuration, createContext()); - TemplateEngine templateEngine = (TemplateEngine) configuration.get(TemplateEngine.class.getName()); - MustacheTemplateEngine mustacheTemplateEngine = (MustacheTemplateEngine) templateEngine; - Map templateContext = (Map) ReflectionTestUtils.getField(mustacheTemplateEngine, - "context"); - assertThat(templateContext.size()).isEqualTo(0); - } - - @Test - void customDefaultOperationRequestPreprocessor() { - Map configuration = new HashMap<>(); - this.configurer.operationPreprocessors() - .withRequestDefaults(Preprocessors.prettyPrint(), Preprocessors.modifyHeaders().remove("Foo")) - .apply(configuration, createContext()); - OperationRequestPreprocessor preprocessor = (OperationRequestPreprocessor) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR); - HttpHeaders headers = new HttpHeaders(); - headers.add("Foo", "value"); - OperationRequest request = new OperationRequestFactory().create(URI.create("http://localhost:8080"), - HttpMethod.GET, null, headers, null, Collections.emptyList()); - assertThat(preprocessor.preprocess(request).getHeaders().headerNames()).doesNotContain("Foo"); - } - - @Test - void customDefaultOperationResponsePreprocessor() { - Map configuration = new HashMap<>(); - this.configurer.operationPreprocessors() - .withResponseDefaults(Preprocessors.prettyPrint(), Preprocessors.modifyHeaders().remove("Foo")) - .apply(configuration, createContext()); - OperationResponsePreprocessor preprocessor = (OperationResponsePreprocessor) configuration - .get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR); - HttpHeaders headers = new HttpHeaders(); - headers.add("Foo", "value"); - OperationResponse response = new OperationResponseFactory().create(HttpStatus.OK, headers, null); - assertThat(preprocessor.preprocess(response).getHeaders().headerNames()).doesNotContain("Foo"); - } - - private RestDocumentationContext createContext() { - ManualRestDocumentation manualRestDocumentation = new ManualRestDocumentation("build"); - manualRestDocumentation.beforeTest(null, null); - RestDocumentationContext context = manualRestDocumentation.beforeOperation(); - return context; - } - - private static final class TestRestDocumentationConfigurer extends - RestDocumentationConfigurer { - - private final TestSnippetConfigurer snippetConfigurer = new TestSnippetConfigurer(this); - - private final TestOperationPreprocessorsConfigurer operationPreprocessorsConfigurer = new TestOperationPreprocessorsConfigurer( - this); - - @Override - public TestSnippetConfigurer snippets() { - return this.snippetConfigurer; - } - - @Override - public TestOperationPreprocessorsConfigurer operationPreprocessors() { - return this.operationPreprocessorsConfigurer; - } - - } - - private static final class TestSnippetConfigurer - extends SnippetConfigurer { - - private TestSnippetConfigurer(TestRestDocumentationConfigurer parent) { - super(parent); - } - - } - - private static final class TestOperationPreprocessorsConfigurer extends - OperationPreprocessorsConfigurer { - - protected TestOperationPreprocessorsConfigurer(TestRestDocumentationConfigurer parent) { - super(parent); - } - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java deleted file mode 100644 index c5425c38c..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ConstraintDescriptionsTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link ConstraintDescriptions}. - * - * @author Andy Wilkinson - */ -class ConstraintDescriptionsTests { - - private final ConstraintResolver constraintResolver = mock(ConstraintResolver.class); - - private final ConstraintDescriptionResolver constraintDescriptionResolver = mock( - ConstraintDescriptionResolver.class); - - private final ConstraintDescriptions constraintDescriptions = new ConstraintDescriptions(Constrained.class, - this.constraintResolver, this.constraintDescriptionResolver); - - @Test - void descriptionsForConstraints() { - Constraint constraint1 = new Constraint("constraint1", Collections.emptyMap()); - Constraint constraint2 = new Constraint("constraint2", Collections.emptyMap()); - given(this.constraintResolver.resolveForProperty("foo", Constrained.class)) - .willReturn(Arrays.asList(constraint1, constraint2)); - given(this.constraintDescriptionResolver.resolveDescription(constraint1)).willReturn("Bravo"); - given(this.constraintDescriptionResolver.resolveDescription(constraint2)).willReturn("Alpha"); - assertThat(this.constraintDescriptions.descriptionsForProperty("foo")).containsExactly("Alpha", "Bravo"); - } - - @Test - void emptyListOfDescriptionsWhenThereAreNoConstraints() { - given(this.constraintResolver.resolveForProperty("foo", Constrained.class)) - .willReturn(Collections.emptyList()); - assertThat(this.constraintDescriptions.descriptionsForProperty("foo").size()).isEqualTo(0); - } - - private static final class Constrained { - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java deleted file mode 100644 index 7a977a9a3..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.lang.annotation.Annotation; -import java.math.BigDecimal; -import java.net.URL; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.ListResourceBundle; -import java.util.ResourceBundle; -import java.util.Set; - -import javax.money.MonetaryAmount; - -import jakarta.validation.constraints.AssertFalse; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.DecimalMax; -import jakarta.validation.constraints.DecimalMin; -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.Future; -import jakarta.validation.constraints.FutureOrPresent; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.Negative; -import jakarta.validation.constraints.NegativeOrZero; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Null; -import jakarta.validation.constraints.Past; -import jakarta.validation.constraints.PastOrPresent; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Positive; -import jakarta.validation.constraints.PositiveOrZero; -import jakarta.validation.constraints.Size; -import org.hibernate.validator.constraints.CodePointLength; -import org.hibernate.validator.constraints.CreditCardNumber; -import org.hibernate.validator.constraints.Currency; -import org.hibernate.validator.constraints.EAN; -import org.hibernate.validator.constraints.Length; -import org.hibernate.validator.constraints.LuhnCheck; -import org.hibernate.validator.constraints.Mod10Check; -import org.hibernate.validator.constraints.Mod11Check; -import org.hibernate.validator.constraints.Range; -import org.junit.jupiter.api.Test; - -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ResourceBundleConstraintDescriptionResolver}. - * - * @author Andy Wilkinson - */ -class ResourceBundleConstraintDescriptionResolverTests { - - private final ResourceBundleConstraintDescriptionResolver resolver = new ResourceBundleConstraintDescriptionResolver(); - - @Test - void defaultMessageAssertFalse() { - assertThat(constraintDescriptionForField("assertFalse")).isEqualTo("Must be false"); - } - - @Test - void defaultMessageAssertTrue() { - assertThat(constraintDescriptionForField("assertTrue")).isEqualTo("Must be true"); - } - - @Test - void defaultMessageCodePointLength() { - assertThat(constraintDescriptionForField("codePointLength")) - .isEqualTo("Code point length must be between 2 and 5 inclusive"); - } - - @Test - void defaultMessageCurrency() { - assertThat(constraintDescriptionForField("currency")) - .isEqualTo("Must be in an accepted currency unit (GBP, USD)"); - } - - @Test - void defaultMessageDecimalMax() { - assertThat(constraintDescriptionForField("decimalMax")).isEqualTo("Must be at most 9.875"); - } - - @Test - void defaultMessageDecimalMin() { - assertThat(constraintDescriptionForField("decimalMin")).isEqualTo("Must be at least 1.5"); - } - - @Test - void defaultMessageDigits() { - assertThat(constraintDescriptionForField("digits")) - .isEqualTo("Must have at most 2 integral digits and 5 fractional digits"); - } - - @Test - void defaultMessageFuture() { - assertThat(constraintDescriptionForField("future")).isEqualTo("Must be in the future"); - } - - @Test - void defaultMessageFutureOrPresent() { - assertThat(constraintDescriptionForField("futureOrPresent")).isEqualTo("Must be in the future or the present"); - } - - @Test - void defaultMessageMax() { - assertThat(constraintDescriptionForField("max")).isEqualTo("Must be at most 10"); - } - - @Test - void defaultMessageMin() { - assertThat(constraintDescriptionForField("min")).isEqualTo("Must be at least 10"); - } - - @Test - void defaultMessageNotNull() { - assertThat(constraintDescriptionForField("notNull")).isEqualTo("Must not be null"); - } - - @Test - void defaultMessageNull() { - assertThat(constraintDescriptionForField("nul")).isEqualTo("Must be null"); - } - - @Test - void defaultMessagePast() { - assertThat(constraintDescriptionForField("past")).isEqualTo("Must be in the past"); - } - - @Test - void defaultMessagePastOrPresent() { - assertThat(constraintDescriptionForField("pastOrPresent")).isEqualTo("Must be in the past or the present"); - } - - @Test - void defaultMessagePattern() { - assertThat(constraintDescriptionForField("pattern")) - .isEqualTo("Must match the regular expression `[A-Z][a-z]+`"); - } - - @Test - void defaultMessageSize() { - assertThat(constraintDescriptionForField("size")).isEqualTo("Size must be between 2 and 10 inclusive"); - } - - @Test - void defaultMessageCreditCardNumber() { - assertThat(constraintDescriptionForField("creditCardNumber")) - .isEqualTo("Must be a well-formed credit card number"); - } - - @Test - void defaultMessageEan() { - assertThat(constraintDescriptionForField("ean")).isEqualTo("Must be a well-formed EAN13 number"); - } - - @Test - void defaultMessageEmail() { - assertThat(constraintDescriptionForField("email")).isEqualTo("Must be a well-formed email address"); - } - - @Test - void defaultMessageLength() { - assertThat(constraintDescriptionForField("length")).isEqualTo("Length must be between 2 and 10 inclusive"); - } - - @Test - void defaultMessageLuhnCheck() { - assertThat(constraintDescriptionForField("luhnCheck")) - .isEqualTo("Must pass the Luhn Modulo 10 checksum algorithm"); - } - - @Test - void defaultMessageMod10Check() { - assertThat(constraintDescriptionForField("mod10Check")).isEqualTo("Must pass the Mod10 checksum algorithm"); - } - - @Test - void defaultMessageMod11Check() { - assertThat(constraintDescriptionForField("mod11Check")).isEqualTo("Must pass the Mod11 checksum algorithm"); - } - - @Test - void defaultMessageNegative() { - assertThat(constraintDescriptionForField("negative")).isEqualTo("Must be negative"); - } - - @Test - void defaultMessageNegativeOrZero() { - assertThat(constraintDescriptionForField("negativeOrZero")).isEqualTo("Must be negative or zero"); - } - - @Test - void defaultMessageNotBlank() { - assertThat(constraintDescriptionForField("notBlank")).isEqualTo("Must not be blank"); - } - - @Test - void defaultMessageNotEmpty() { - assertThat(constraintDescriptionForField("notEmpty")).isEqualTo("Must not be empty"); - } - - @Test - void defaultMessageNotEmptyHibernateValidator() { - assertThat(constraintDescriptionForField("notEmpty")).isEqualTo("Must not be empty"); - } - - @Test - void defaultMessagePositive() { - assertThat(constraintDescriptionForField("positive")).isEqualTo("Must be positive"); - } - - @Test - void defaultMessagePositiveOrZero() { - assertThat(constraintDescriptionForField("positiveOrZero")).isEqualTo("Must be positive or zero"); - } - - @Test - void defaultMessageRange() { - assertThat(constraintDescriptionForField("range")).isEqualTo("Must be at least 10 and at most 100"); - } - - @Test - void defaultMessageUrl() { - assertThat(constraintDescriptionForField("url")).isEqualTo("Must be a well-formed URL"); - } - - @Test - void customMessage() { - Thread.currentThread().setContextClassLoader(new ClassLoader() { - - @Override - public URL getResource(String name) { - if (name.startsWith("org/springframework/restdocs/constraints/ConstraintDescriptions")) { - return super.getResource( - "org/springframework/restdocs/constraints/TestConstraintDescriptions.properties"); - } - return super.getResource(name); - } - - }); - - try { - String description = new ResourceBundleConstraintDescriptionResolver() - .resolveDescription(new Constraint(NotNull.class.getName(), Collections.emptyMap())); - assertThat(description).isEqualTo("Should not be null"); - - } - finally { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - } - - @Test - void customResourceBundle() { - ResourceBundle bundle = new ListResourceBundle() { - - @Override - protected Object[][] getContents() { - return new String[][] { { NotNull.class.getName() + ".description", "Not null" } }; - } - - }; - String description = new ResourceBundleConstraintDescriptionResolver(bundle) - .resolveDescription(new Constraint(NotNull.class.getName(), Collections.emptyMap())); - assertThat(description).isEqualTo("Not null"); - } - - @Test - void allBeanValidationConstraintsAreTested() throws Exception { - PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); - Resource[] resources = resolver.getResources("jakarta/validation/constraints/*.class"); - Set> beanValidationConstraints = new HashSet<>(); - for (Resource resource : resources) { - String className = ClassUtils.convertResourcePathToClassName(((ClassPathResource) resource).getPath()); - if (className.endsWith(".class")) { - className = className.substring(0, className.length() - 6); - } - Class type = Class.forName(className); - if (type.isAnnotation() && type.isAnnotationPresent(jakarta.validation.Constraint.class)) { - beanValidationConstraints.add(type); - } - } - ReflectionUtils.doWithFields(Constrained.class, (field) -> { - for (Annotation annotation : field.getAnnotations()) { - beanValidationConstraints.remove(annotation.annotationType()); - } - }); - assertThat(beanValidationConstraints).isEmpty(); - } - - private String constraintDescriptionForField(String name) { - return this.resolver.resolveDescription(getConstraintFromField(name)); - } - - private Constraint getConstraintFromField(String name) { - Annotation[] annotations = ReflectionUtils.findField(Constrained.class, name).getAnnotations(); - Assert.isTrue(annotations.length == 1, - "The field '" + name + "' must have " + "exactly one @Constrained annotation"); - return new Constraint(annotations[0].annotationType().getName(), - AnnotationUtils.getAnnotationAttributes(annotations[0])); - } - - private static final class Constrained { - - @AssertFalse - private boolean assertFalse; - - @AssertTrue - private boolean assertTrue; - - @CodePointLength(min = 2, max = 5) - private String codePointLength; - - @Currency({ "GBP", "USD" }) - private MonetaryAmount currency; - - @DecimalMax("9.875") - private BigDecimal decimalMax; - - @DecimalMin("1.5") - private BigDecimal decimalMin; - - @Digits(integer = 2, fraction = 5) - private String digits; - - @Future - private Date future; - - @FutureOrPresent - private Date futureOrPresent; - - @Max(10) - private int max; - - @Min(10) - private int min; - - @NotNull - private String notNull; - - @Null - private String nul; - - @Past - private Date past; - - @PastOrPresent - private Date pastOrPresent; - - @Pattern(regexp = "[A-Z][a-z]+") - private String pattern; - - @Size(min = 2, max = 10) - private List size; - - @CreditCardNumber - private String creditCardNumber; - - @EAN - private String ean; - - @Email - private String email; - - @Length(min = 2, max = 10) - private String length; - - @LuhnCheck - private String luhnCheck; - - @Mod10Check - private String mod10Check; - - @Mod11Check - private String mod11Check; - - @Negative - private int negative; - - @NegativeOrZero - private int negativeOrZero; - - @NotBlank - private String notBlank; - - @NotEmpty - private String notEmpty; - - @Positive - private int positive; - - @PositiveOrZero - private int positiveOrZero; - - @Range(min = 10, max = 100) - private int range; - - @org.hibernate.validator.constraints.URL - private String url; - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java deleted file mode 100644 index 203817f81..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ValidatorConstraintResolverTests.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.constraints; - -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import jakarta.validation.Payload; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Null; -import jakarta.validation.constraints.Size; -import org.assertj.core.api.Condition; -import org.assertj.core.description.TextDescription; -import org.hibernate.validator.constraints.CompositionType; -import org.hibernate.validator.constraints.ConstraintComposition; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ValidatorConstraintResolver}. - * - * @author Andy Wilkinson - */ -class ValidatorConstraintResolverTests { - - private final ValidatorConstraintResolver resolver = new ValidatorConstraintResolver(); - - @Test - void singleFieldConstraint() { - List constraints = this.resolver.resolveForProperty("single", ConstrainedFields.class); - assertThat(constraints).hasSize(1); - assertThat(constraints.get(0).getName()).isEqualTo(NotNull.class.getName()); - } - - @Test - void multipleFieldConstraints() { - List constraints = this.resolver.resolveForProperty("multiple", ConstrainedFields.class); - assertThat(constraints).hasSize(2); - assertThat(constraints.get(0)).is(constraint(NotNull.class)); - assertThat(constraints.get(1)).is(constraint(Size.class).config("min", 8).config("max", 16)); - } - - @Test - void noFieldConstraints() { - List constraints = this.resolver.resolveForProperty("none", ConstrainedFields.class); - assertThat(constraints).hasSize(0); - } - - @Test - void compositeConstraint() { - List constraints = this.resolver.resolveForProperty("composite", ConstrainedFields.class); - assertThat(constraints).hasSize(1); - } - - private ConstraintCondition constraint(final Class annotation) { - return new ConstraintCondition(annotation); - } - - private static final class ConstrainedFields { - - @NotNull - private String single; - - @NotNull - @Size(min = 8, max = 16) - private String multiple; - - @SuppressWarnings("unused") - private String none; - - @CompositeConstraint - private String composite; - - } - - @ConstraintComposition(CompositionType.OR) - @Null - @NotBlank - @Target(ElementType.FIELD) - @Retention(RetentionPolicy.RUNTIME) - @jakarta.validation.Constraint(validatedBy = {}) - private @interface CompositeConstraint { - - String message() default "Must be null or not blank"; - - Class[] groups() default {}; - - Class[] payload() default {}; - - } - - private static final class ConstraintCondition extends Condition { - - private final Class annotation; - - private final Map configuration = new HashMap<>(); - - private ConstraintCondition(Class annotation) { - this.annotation = annotation; - as(new TextDescription("Constraint named %s with configuration %s", this.annotation, this.configuration)); - } - - private ConstraintCondition config(String key, Object value) { - this.configuration.put(key, value); - return this; - } - - @Override - public boolean matches(Constraint constraint) { - if (!constraint.getName().equals(this.annotation.getName())) { - return false; - } - for (Entry entry : this.configuration.entrySet()) { - if (!constraint.getConfiguration().get(entry.getKey()).equals(entry.getValue())) { - return false; - } - } - return true; - } - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java deleted file mode 100644 index febac6e00..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/RequestCookiesSnippetTests.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link RequestCookiesSnippet}. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - */ -class RequestCookiesSnippetTests { - - @RenderedSnippetTest - void requestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestCookiesSnippet( - Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two"))) - .document(operationBuilder.request("http://localhost") - .cookie("tz", "Europe%2FLondon") - .cookie("logged_in", "true") - .build()); - assertThat(snippets.requestCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); - } - - @RenderedSnippetTest - void ignoredRequestCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestCookiesSnippet( - Arrays.asList(cookieWithName("tz").ignored(), cookieWithName("logged_in").description("two"))) - .document(operationBuilder.request("http://localhost") - .cookie("tz", "Europe%2FLondon") - .cookie("logged_in", "true") - .build()); - assertThat(snippets.requestCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`logged_in`", "two")); - } - - @RenderedSnippetTest - void allUndocumentedCookiesCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestCookiesSnippet( - Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")), - true) - .document(operationBuilder.request("http://localhost") - .cookie("tz", "Europe%2FLondon") - .cookie("logged_in", "true") - .cookie("user_session", "abcd1234efgh5678") - .build()); - assertThat(snippets.requestCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); - } - - @RenderedSnippetTest - void missingOptionalCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestCookiesSnippet(Arrays.asList(cookieWithName("tz").description("one").optional(), - cookieWithName("logged_in").description("two"))) - .document(operationBuilder.request("http://localhost").cookie("logged_in", "true").build()); - assertThat(snippets.requestCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`tz`", "one").row("`logged_in`", "two")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-cookies", template = "request-cookies-with-title") - void requestCookiesWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestCookiesSnippet(Collections.singletonList(cookieWithName("tz").description("one")), - attributes(key("title").value("Custom title"))) - .document(operationBuilder.request("http://localhost").cookie("tz", "Europe%2FLondon").build()); - assertThat(snippets.requestCookies()).contains("Custom title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-cookies", template = "request-cookies-with-extra-column") - void requestCookiesWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestCookiesSnippet( - Arrays.asList(cookieWithName("tz").description("one").attributes(key("foo").value("alpha")), - cookieWithName("logged_in").description("two").attributes(key("foo").value("bravo")))) - .document(operationBuilder.request("http://localhost") - .cookie("tz", "Europe%2FLondon") - .cookie("logged_in", "true") - .build()); - assertThat(snippets.requestCookies()).isTable((table) -> table.withHeader("Name", "Description", "Foo") - .row("tz", "one", "alpha") - .row("logged_in", "two", "bravo")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestCookiesSnippet( - Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two"))) - .and(cookieWithName("user_session").description("three")) - .document(operationBuilder.request("http://localhost") - .cookie("tz", "Europe%2FLondon") - .cookie("logged_in", "true") - .cookie("user_session", "abcd1234efgh5678") - .build()); - assertThat(snippets.requestCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`tz`", "one") - .row("`logged_in`", "two") - .row("`user_session`", "three")); - } - - @RenderedSnippetTest - void additionalDescriptorsWithRelaxedRequestCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestCookiesSnippet( - Arrays.asList(cookieWithName("tz").description("one"), cookieWithName("logged_in").description("two")), - true) - .and(cookieWithName("user_session").description("three")) - .document(operationBuilder.request("http://localhost") - .cookie("tz", "Europe%2FLondon") - .cookie("logged_in", "true") - .cookie("user_session", "abcd1234efgh5678") - .cookie("color_theme", "light") - .build()); - assertThat(snippets.requestCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`tz`", "one") - .row("`logged_in`", "two") - .row("`user_session`", "three")); - } - - @RenderedSnippetTest - void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestCookiesSnippet(Collections.singletonList(cookieWithName("Foo|Bar").description("one|two"))) - .document(operationBuilder.request("http://localhost").cookie("Foo|Bar", "baz").build()); - assertThat(snippets.requestCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void missingRequestCookie(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestCookiesSnippet( - Collections.singletonList(CookieDocumentation.cookieWithName("JSESSIONID").description("one"))) - .document(operationBuilder.request("http://localhost").build())) - .withMessage("Cookies with the following names were not found in the request: [JSESSIONID]"); - } - - @SnippetTest - void undocumentedRequestCookie(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestCookiesSnippet(Collections.emptyList()).document( - operationBuilder.request("http://localhost").cookie("JSESSIONID", "1234abcd5678efgh").build())) - .withMessageEndingWith("Cookies with the following names were not documented: [JSESSIONID]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java deleted file mode 100644 index 2d7883e45..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cookies/ResponseCookiesSnippetTests.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.cookies; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link ResponseCookiesSnippet}. - * - * @author Clyde Stubbs - * @author Andy Wilkinson - */ -class ResponseCookiesSnippetTests { - - @RenderedSnippetTest - void responseWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one"), - cookieWithName("user_session").description("two"))) - .document(operationBuilder.response() - .cookie("has_recent_activity", "true") - .cookie("user_session", "1234abcd5678efgh") - .build()); - assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`has_recent_activity`", "one") - .row("`user_session`", "two")); - } - - @RenderedSnippetTest - void ignoredResponseCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").ignored(), - cookieWithName("user_session").description("two"))) - .document(operationBuilder.response() - .cookie("has_recent_activity", "true") - .cookie("user_session", "1234abcd5678efgh") - .build()); - assertThat(snippets.responseCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`user_session`", "two")); - } - - @RenderedSnippetTest - void allUndocumentedResponseCookiesCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one"), - cookieWithName("user_session").description("two")), true) - .document(operationBuilder.response() - .cookie("has_recent_activity", "true") - .cookie("user_session", "1234abcd5678efgh") - .cookie("some_cookie", "value") - .build()); - assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`has_recent_activity`", "one") - .row("`user_session`", "two")); - } - - @RenderedSnippetTest - void missingOptionalResponseCookie(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseCookiesSnippet(Arrays.asList(cookieWithName("has_recent_activity").description("one").optional(), - cookieWithName("user_session").description("two"))) - .document(operationBuilder.response().cookie("user_session", "1234abcd5678efgh").build()); - assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`has_recent_activity`", "one") - .row("`user_session`", "two")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-cookies", template = "response-cookies-with-title") - void responseCookiesWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseCookiesSnippet(Collections.singletonList(cookieWithName("has_recent_activity").description("one")), - attributes(key("title").value("Custom title"))) - .document(operationBuilder.response().cookie("has_recent_activity", "true").build()); - assertThat(snippets.responseCookies()).contains("Custom title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-cookies", template = "response-cookies-with-extra-column") - void responseCookiesWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseCookiesSnippet(Arrays.asList( - cookieWithName("has_recent_activity").description("one").attributes(key("foo").value("alpha")), - cookieWithName("user_session").description("two").attributes(key("foo").value("bravo")), - cookieWithName("color_theme").description("three").attributes(key("foo").value("charlie")))) - .document(operationBuilder.response() - .cookie("has_recent_activity", "true") - .cookie("user_session", "1234abcd5678efgh") - .cookie("color_theme", "high_contrast") - .build()); - assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description", "Foo") - .row("has_recent_activity", "one", "alpha") - .row("user_session", "two", "bravo") - .row("color_theme", "three", "charlie")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - CookieDocumentation - .responseCookies(cookieWithName("has_recent_activity").description("one"), - cookieWithName("user_session").description("two")) - .and(cookieWithName("color_theme").description("three")) - .document(operationBuilder.response() - .cookie("has_recent_activity", "true") - .cookie("user_session", "1234abcd5678efgh") - .cookie("color_theme", "light") - .build()); - assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`has_recent_activity`", "one") - .row("`user_session`", "two") - .row("`color_theme`", "three")); - } - - @RenderedSnippetTest - void additionalDescriptorsWithRelaxedResponseCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - CookieDocumentation.relaxedResponseCookies(cookieWithName("has_recent_activity").description("one")) - .and(cookieWithName("color_theme").description("two")) - .document(operationBuilder.response() - .cookie("has_recent_activity", "true") - .cookie("user_session", "1234abcd5678efgh") - .cookie("color_theme", "light") - .build()); - assertThat(snippets.responseCookies()).isTable((table) -> table.withHeader("Name", "Description") - .row("`has_recent_activity`", "one") - .row("`color_theme`", "two")); - } - - @RenderedSnippetTest - void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseCookiesSnippet(Collections.singletonList(cookieWithName("Foo|Bar").description("one|two"))) - .document(operationBuilder.response().cookie("Foo|Bar", "baz").build()); - assertThat(snippets.responseCookies()) - .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java deleted file mode 100644 index 51f16ccc3..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/RequestHeadersSnippetTests.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.io.IOException; -import java.util.Arrays; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link RequestHeadersSnippet}. - * - * @author Andreas Evers - * @author Andy Wilkinson - */ -class RequestHeadersSnippetTests { - - @RenderedSnippetTest - void requestWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"), - headerWithName("Accept").description("two"), headerWithName("Accept-Encoding").description("three"), - headerWithName("Accept-Language").description("four"), - headerWithName("Cache-Control").description("five"), headerWithName("Connection").description("six"))) - .document(operationBuilder.request("http://localhost") - .header("X-Test", "test") - .header("Accept", "*/*") - .header("Accept-Encoding", "gzip, deflate") - .header("Accept-Language", "en-US,en;q=0.5") - .header("Cache-Control", "max-age=0") - .header("Connection", "keep-alive") - .build()); - assertThat(snippets.requestHeaders()).isTable((table) -> table.withHeader("Name", "Description") - .row("`X-Test`", "one") - .row("`Accept`", "two") - .row("`Accept-Encoding`", "three") - .row("`Accept-Language`", "four") - .row("`Cache-Control`", "five") - .row("`Connection`", "six")); - } - - @RenderedSnippetTest - void caseInsensitiveRequestHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(operationBuilder.request("/").header("X-test", "test").build()); - assertThat(snippets.requestHeaders()) - .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); - } - - @RenderedSnippetTest - void undocumentedRequestHeader(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))).document( - operationBuilder.request("http://localhost").header("X-Test", "test").header("Accept", "*/*").build()); - assertThat(snippets.requestHeaders()) - .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-headers", template = "request-headers-with-title") - void requestHeadersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one")), - attributes(key("title").value("Custom title"))) - .document(operationBuilder.request("http://localhost").header("X-Test", "test").build()); - assertThat(snippets.requestHeaders()).contains("Custom title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-headers", template = "request-headers-with-extra-column") - void requestHeadersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestHeadersSnippet( - Arrays.asList(headerWithName("X-Test").description("one").attributes(key("foo").value("alpha")), - headerWithName("Accept-Encoding").description("two").attributes(key("foo").value("bravo")), - headerWithName("Accept").description("three").attributes(key("foo").value("charlie")))) - .document(operationBuilder.request("http://localhost") - .header("X-Test", "test") - .header("Accept-Encoding", "gzip, deflate") - .header("Accept", "*/*") - .build()); - assertThat(snippets.requestHeaders()).isTable((table) -> table.withHeader("Name", "Description", "Foo") - .row("X-Test", "one", "alpha") - .row("Accept-Encoding", "two", "bravo") - .row("Accept", "three", "charlie")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - HeaderDocumentation - .requestHeaders(headerWithName("X-Test").description("one"), headerWithName("Accept").description("two"), - headerWithName("Accept-Encoding").description("three"), - headerWithName("Accept-Language").description("four")) - .and(headerWithName("Cache-Control").description("five"), headerWithName("Connection").description("six")) - .document(operationBuilder.request("http://localhost") - .header("X-Test", "test") - .header("Accept", "*/*") - .header("Accept-Encoding", "gzip, deflate") - .header("Accept-Language", "en-US,en;q=0.5") - .header("Cache-Control", "max-age=0") - .header("Connection", "keep-alive") - .build()); - assertThat(snippets.requestHeaders()).isTable((table) -> table.withHeader("Name", "Description") - .row("`X-Test`", "one") - .row("`Accept`", "two") - .row("`Accept-Encoding`", "three") - .row("`Accept-Language`", "four") - .row("`Cache-Control`", "five") - .row("`Connection`", "six")); - } - - @RenderedSnippetTest - void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestHeadersSnippet(Arrays.asList(headerWithName("Foo|Bar").description("one|two"))) - .document(operationBuilder.request("http://localhost").header("Foo|Bar", "baz").build()); - assertThat(snippets.requestHeaders()) - .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void missingRequestHeader(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description("one"))) - .document(operationBuilder.request("http://localhost").build())) - .withMessage("Headers with the following names were not found in the request: [Accept]"); - } - - @SnippetTest - void undocumentedRequestHeaderAndMissingRequestHeader(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description("one"))) - .document(operationBuilder.request("http://localhost").header("X-Test", "test").build())) - .withMessageEndingWith("Headers with the following names were not found in the request: [Accept]"); - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java deleted file mode 100644 index 484ee4cbc..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/headers/ResponseHeadersSnippetTests.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.headers; - -import java.io.IOException; -import java.util.Arrays; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link ResponseHeadersSnippet}. - * - * @author Andreas Evers - * @author Andy Wilkinson - */ -class ResponseHeadersSnippetTests { - - @RenderedSnippetTest - void responseWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"), - headerWithName("Content-Type").description("two"), headerWithName("Etag").description("three"), - headerWithName("Cache-Control").description("five"), headerWithName("Vary").description("six"))) - .document(operationBuilder.response() - .header("X-Test", "test") - .header("Content-Type", "application/json") - .header("Etag", "lskjadldj3ii32l2ij23") - .header("Cache-Control", "max-age=0") - .header("Vary", "User-Agent") - .build()); - assertThat(snippets.responseHeaders()).isTable((table) -> table.withHeader("Name", "Description") - .row("`X-Test`", "one") - .row("`Content-Type`", "two") - .row("`Etag`", "three") - .row("`Cache-Control`", "five") - .row("`Vary`", "six")); - } - - @RenderedSnippetTest - void caseInsensitiveResponseHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(operationBuilder.response().header("X-test", "test").build()); - assertThat(snippets.responseHeaders()) - .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); - } - - @RenderedSnippetTest - void undocumentedResponseHeader(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one"))) - .document(operationBuilder.response().header("X-Test", "test").header("Content-Type", "*/*").build()); - assertThat(snippets.responseHeaders()) - .isTable((table) -> table.withHeader("Name", "Description").row("`X-Test`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-headers", template = "response-headers-with-title") - void responseHeadersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description("one")), - attributes(key("title").value("Custom title"))) - .document(operationBuilder.response().header("X-Test", "test").build()); - assertThat(snippets.responseHeaders()).contains("Custom title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-headers", template = "response-headers-with-extra-column") - void responseHeadersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseHeadersSnippet( - Arrays.asList(headerWithName("X-Test").description("one").attributes(key("foo").value("alpha")), - headerWithName("Content-Type").description("two").attributes(key("foo").value("bravo")), - headerWithName("Etag").description("three").attributes(key("foo").value("charlie")))) - .document(operationBuilder.response() - .header("X-Test", "test") - .header("Content-Type", "application/json") - .header("Etag", "lskjadldj3ii32l2ij23") - .build()); - assertThat(snippets.responseHeaders()).isTable((table) -> table.withHeader("Name", "Description", "Foo") - .row("X-Test", "one", "alpha") - .row("Content-Type", "two", "bravo") - .row("Etag", "three", "charlie")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - HeaderDocumentation - .responseHeaders(headerWithName("X-Test").description("one"), - headerWithName("Content-Type").description("two"), headerWithName("Etag").description("three")) - .and(headerWithName("Cache-Control").description("five"), headerWithName("Vary").description("six")) - .document(operationBuilder.response() - .header("X-Test", "test") - .header("Content-Type", "application/json") - .header("Etag", "lskjadldj3ii32l2ij23") - .header("Cache-Control", "max-age=0") - .header("Vary", "User-Agent") - .build()); - assertThat(snippets.responseHeaders()).isTable((table) -> table.withHeader("Name", "Description") - .row("`X-Test`", "one") - .row("`Content-Type`", "two") - .row("`Etag`", "three") - .row("`Cache-Control`", "five") - .row("`Vary`", "six")); - } - - @RenderedSnippetTest - void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseHeadersSnippet(Arrays.asList(headerWithName("Foo|Bar").description("one|two"))) - .document(operationBuilder.response().header("Foo|Bar", "baz").build()); - assertThat(snippets.responseHeaders()) - .isTable((table) -> table.withHeader("Name", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void missingResponseHeader(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy( - () -> new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type").description("one"))) - .document(operationBuilder.response().build())) - .withMessage("Headers with the following names were not found" + " in the response: [Content-Type]"); - } - - @SnippetTest - void undocumentedResponseHeaderAndMissingResponseHeader(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy( - () -> new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type").description("one"))) - .document(operationBuilder.response().header("X-Test", "test").build())) - .withMessageEndingWith("Headers with the following names were not found in the response: [Content-Type]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java deleted file mode 100644 index a37e3fd3c..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.http; - -import java.io.IOException; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link HttpRequestSnippet}. - * - * @author Andy Wilkinson - * @author Jonathan Pearlin - */ -class HttpRequestSnippetTests { - - private static final String BOUNDARY = "6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm"; - - @RenderedSnippetTest - void getRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost/foo").header("Alpha", "a").build()); - assertThat(snippets.httpRequest()) - .isHttpRequest((request) -> request.get("/foo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost")); - } - - @RenderedSnippetTest - void getRequestWithQueryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost/foo?b=bravo").header("Alpha", "a").build()); - assertThat(snippets.httpRequest()).isHttpRequest( - (request) -> request.get("/foo?b=bravo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost")); - } - - @RenderedSnippetTest - void getRequestWithPort(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost:8080/foo").header("Alpha", "a").build()); - assertThat(snippets.httpRequest()).isHttpRequest( - (request) -> request.get("/foo").header("Alpha", "a").header(HttpHeaders.HOST, "localhost:8080")); - } - - @RenderedSnippetTest - void getRequestWithCookies(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo") - .cookie("name1", "value1") - .cookie("name2", "value2") - .build()); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.get("/foo") - .header(HttpHeaders.HOST, "localhost") - .header(HttpHeaders.COOKIE, "name1=value1; name2=value2")); - } - - @RenderedSnippetTest - void getRequestWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo?bar=baz").build()); - assertThat(snippets.httpRequest()) - .isHttpRequest((request) -> request.get("/foo?bar=baz").header(HttpHeaders.HOST, "localhost")); - } - - @RenderedSnippetTest - void getRequestWithQueryStringWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo?bar").build()); - assertThat(snippets.httpRequest()) - .isHttpRequest((request) -> request.get("/foo?bar").header(HttpHeaders.HOST, "localhost")); - } - - @RenderedSnippetTest - void postRequestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - String content = "Hello, world"; - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost/foo").method("POST").content(content).build()); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/foo") - .header(HttpHeaders.HOST, "localhost") - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - } - - @RenderedSnippetTest - void postRequestWithContentAndQueryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - String content = "Hello, world"; - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost/foo?a=alpha").method("POST").content(content).build()); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/foo?a=alpha") - .header(HttpHeaders.HOST, "localhost") - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - - } - - @RenderedSnippetTest - void postRequestWithCharset(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; - byte[] contentBytes = japaneseContent.getBytes("UTF-8"); - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/foo") - .method("POST") - .header("Content-Type", "text/plain;charset=UTF-8") - .content(contentBytes) - .build()); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/foo") - .header("Content-Type", "text/plain;charset=UTF-8") - .header(HttpHeaders.HOST, "localhost") - .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length) - .content(japaneseContent)); - } - - @RenderedSnippetTest - void putRequestWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - String content = "Hello, world"; - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost/foo").method("PUT").content(content).build()); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.put("/foo") - .header(HttpHeaders.HOST, "localhost") - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - } - - @RenderedSnippetTest - void multipartPost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", "<< data >>".getBytes()) - .build()); - String expectedContent = createPart( - String.format("Content-Disposition: " + "form-data; " + "name=image%n%n<< data >>")); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/upload") - .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .header(HttpHeaders.HOST, "localhost") - .content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPut(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") - .method("PUT") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", "<< data >>".getBytes()) - .build()); - String expectedContent = createPart( - String.format("Content-Disposition: " + "form-data; " + "name=image%n%n<< data >>")); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.put("/upload") - .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .header(HttpHeaders.HOST, "localhost") - .content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPatch(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") - .method("PATCH") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", "<< data >>".getBytes()) - .build()); - String expectedContent = createPart( - String.format("Content-Disposition: " + "form-data; " + "name=image%n%n<< data >>")); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.patch("/upload") - .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .header(HttpHeaders.HOST, "localhost") - .content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPostWithFilename(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", "<< data >>".getBytes()) - .submittedFileName("image.png") - .build()); - String expectedContent = createPart(String - .format("Content-Disposition: " + "form-data; " + "name=image; filename=image.png%n%n<< data >>")); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/upload") - .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .header(HttpHeaders.HOST, "localhost") - .content(expectedContent)); - } - - @RenderedSnippetTest - void multipartPostWithContentType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpRequestSnippet().document(operationBuilder.request("http://localhost/upload") - .method("POST") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .part("image", "<< data >>".getBytes()) - .header(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE) - .build()); - String expectedContent = createPart(String - .format("Content-Disposition: form-data; name=image%nContent-Type: " + "image/png%n%n<< data >>")); - assertThat(snippets.httpRequest()).isHttpRequest((request) -> request.post("/upload") - .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .header(HttpHeaders.HOST, "localhost") - .content(expectedContent)); - } - - @RenderedSnippetTest - void getRequestWithCustomHost(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet().document( - operationBuilder.request("http://localhost/foo").header(HttpHeaders.HOST, "api.example.com").build()); - assertThat(snippets.httpRequest()) - .isHttpRequest((request) -> request.get("/foo").header(HttpHeaders.HOST, "api.example.com")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "http-request", template = "http-request-with-title") - void requestWithCustomSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpRequestSnippet(attributes(key("title").value("Title for the request"))) - .document(operationBuilder.request("http://localhost/foo").build()); - assertThat(snippets.httpRequest()).contains("Title for the request"); - } - - @RenderedSnippetTest - void deleteWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpRequestSnippet() - .document(operationBuilder.request("http://localhost/foo?a=alpha&b=bravo").method("DELETE").build()); - assertThat(snippets.httpRequest()) - .isHttpRequest((request) -> request.delete("/foo?a=alpha&b=bravo").header("Host", "localhost")); - } - - private String createPart(String content) { - return this.createPart(content, true); - } - - private String createPart(String content, boolean last) { - StringBuilder part = new StringBuilder(); - part.append(String.format("--%s%n%s%n", BOUNDARY, content)); - if (last) { - part.append(String.format("--%s--", BOUNDARY)); - } - return part.toString(); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java deleted file mode 100644 index 0ce5785ea..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpResponseSnippetTests.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.http; - -import java.io.IOException; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link HttpResponseSnippet}. - * - * @author Andy Wilkinson - * @author Jonathan Pearlin - */ -class HttpResponseSnippetTests { - - @RenderedSnippetTest - void basicResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpResponseSnippet().document(operationBuilder.build()); - assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok()); - } - - @RenderedSnippetTest - void nonOkResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpResponseSnippet().document(operationBuilder.response().status(HttpStatus.BAD_REQUEST).build()); - assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.badRequest()); - } - - @RenderedSnippetTest - void responseWithHeaders(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpResponseSnippet().document(operationBuilder.response() - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .header("a", "alpha") - .build()); - assertThat(snippets.httpResponse()).isHttpResponse( - (response) -> response.ok().header("Content-Type", "application/json").header("a", "alpha")); - } - - @RenderedSnippetTest - void responseWithContent(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - String content = "content"; - new HttpResponseSnippet().document(operationBuilder.response().content(content).build()); - assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok() - .content(content) - .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); - } - - @RenderedSnippetTest - void responseWithCharset(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; - byte[] contentBytes = japaneseContent.getBytes("UTF-8"); - new HttpResponseSnippet().document(operationBuilder.response() - .header("Content-Type", "text/plain;charset=UTF-8") - .content(contentBytes) - .build()); - assertThat(snippets.httpResponse()).isHttpResponse((response) -> response.ok() - .header("Content-Type", "text/plain;charset=UTF-8") - .content(japaneseContent) - .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length)); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "http-response", template = "http-response-with-title") - void responseWithCustomSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new HttpResponseSnippet(attributes(key("title").value("Title for the response"))) - .document(operationBuilder.build()); - assertThat(snippets.httpResponse()).contains("Title for the response"); - } - - @RenderedSnippetTest - void responseWithCustomStatus(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new HttpResponseSnippet().document(operationBuilder.response().status(HttpStatusCode.valueOf(215)).build()); - assertThat(snippets.httpResponse()).isHttpResponse(((response) -> response.status(215))); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java deleted file mode 100644 index eb5875f8b..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link ContentTypeLinkExtractor}. - * - * @author Andy Wilkinson - */ -class ContentTypeLinkExtractorTests { - - private final OperationResponseFactory responseFactory = new OperationResponseFactory(); - - private final String halBody = "{ \"_links\" : { \"someRel\" : { \"href\" : \"someHref\" }} }"; - - @Test - void extractionFailsWithNullContentType() { - assertThatIllegalStateException().isThrownBy(() -> new ContentTypeLinkExtractor() - .extractLinks(this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), null))); - } - - @Test - void extractorCalledWithMatchingContextType() throws IOException { - Map extractors = new HashMap<>(); - LinkExtractor extractor = mock(LinkExtractor.class); - extractors.put(MediaType.APPLICATION_JSON, extractor); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, null); - new ContentTypeLinkExtractor(extractors).extractLinks(response); - verify(extractor).extractLinks(response); - } - - @Test - void extractorCalledWithCompatibleContextType() throws IOException { - Map extractors = new HashMap<>(); - LinkExtractor extractor = mock(LinkExtractor.class); - extractors.put(MediaType.APPLICATION_JSON, extractor); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.parseMediaType("application/json;foo=bar")); - OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, null); - new ContentTypeLinkExtractor(extractors).extractLinks(response); - verify(extractor).extractLinks(response); - } - - @Test - void extractsLinksFromVndHalMediaType() throws IOException { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.parseMediaType("application/vnd.hal+json")); - OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes()); - Map> links = new ContentTypeLinkExtractor().extractLinks(response); - assertThat(links).containsKey("someRel"); - } - - @Test - void extractsLinksFromHalFormsMediaType() throws IOException { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.parseMediaType("application/prs.hal-forms+json")); - OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes()); - Map> links = new ContentTypeLinkExtractor().extractLinks(response); - assertThat(links).containsKey("someRel"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java deleted file mode 100644 index 5346d512c..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedClass; -import org.junit.jupiter.params.provider.MethodSource; - -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link HalLinkExtractor} and {@link AtomLinkExtractor} with various payloads. - * - * @author Andy Wilkinson - */ -@ParameterizedClass(name = "{1}") -@MethodSource("parameters") -class LinkExtractorsPayloadTests { - - private final OperationResponseFactory responseFactory = new OperationResponseFactory(); - - private final LinkExtractor linkExtractor; - - private final String linkType; - - static Collection parameters() { - return Arrays.asList(new Object[] { new HalLinkExtractor(), "hal" }, - new Object[] { new AtomLinkExtractor(), "atom" }); - } - - LinkExtractorsPayloadTests(LinkExtractor linkExtractor, String linkType) { - this.linkExtractor = linkExtractor; - this.linkType = linkType; - } - - @Test - void singleLink() throws IOException { - Map> links = this.linkExtractor.extractLinks(createResponse("single-link")); - assertLinks(Arrays.asList(new Link("alpha", "https://alpha.example.com", "Alpha")), links); - } - - @Test - void multipleLinksWithDifferentRels() throws IOException { - Map> links = this.linkExtractor - .extractLinks(createResponse("multiple-links-different-rels")); - assertLinks(Arrays.asList(new Link("alpha", "https://alpha.example.com", "Alpha"), - new Link("bravo", "https://bravo.example.com")), links); - } - - @Test - void multipleLinksWithSameRels() throws IOException { - Map> links = this.linkExtractor.extractLinks(createResponse("multiple-links-same-rels")); - assertLinks(Arrays.asList(new Link("alpha", "https://alpha.example.com/one", "Alpha one"), - new Link("alpha", "https://alpha.example.com/two")), links); - } - - @Test - void noLinks() throws IOException { - Map> links = this.linkExtractor.extractLinks(createResponse("no-links")); - assertLinks(Collections.emptyList(), links); - } - - @Test - void linksInTheWrongFormat() throws IOException { - Map> links = this.linkExtractor.extractLinks(createResponse("wrong-format")); - assertLinks(Collections.emptyList(), links); - } - - private void assertLinks(List expectedLinks, Map> actualLinks) { - MultiValueMap expectedLinksByRel = new LinkedMultiValueMap<>(); - for (Link expectedLink : expectedLinks) { - expectedLinksByRel.add(expectedLink.getRel(), expectedLink); - } - assertThat(actualLinks).isEqualTo(expectedLinksByRel); - } - - private OperationResponse createResponse(String contentName) throws IOException { - return this.responseFactory.create(HttpStatus.OK, null, - FileCopyUtils.copyToByteArray(getPayloadFile(contentName))); - } - - private File getPayloadFile(String name) { - return new File("src/test/resources/link-payloads/" + this.linkType + "/" + name + ".json"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java deleted file mode 100644 index 7782ce42f..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinksSnippetTests.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link LinksSnippet}. - * - * @author Andy Wilkinson - */ -class LinksSnippetTests { - - @RenderedSnippetTest - void ignoredLink(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), - Arrays.asList(new LinkDescriptor("a").ignored(), new LinkDescriptor("b").description("Link b"))) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`b`", "Link b")); - } - - @RenderedSnippetTest - void allUndocumentedLinksCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), - Arrays.asList(new LinkDescriptor("b").description("Link b")), true) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`b`", "Link b")); - } - - @RenderedSnippetTest - void presentOptionalLink(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "blah")), - Arrays.asList(new LinkDescriptor("foo").description("bar").optional())) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`foo`", "bar")); - } - - @RenderedSnippetTest - void missingOptionalLink(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new LinksSnippet(new StubLinkExtractor(), - Arrays.asList(new LinkDescriptor("foo").description("bar").optional())) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`foo`", "bar")); - } - - @RenderedSnippetTest - void documentedLinks(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), - Arrays.asList(new LinkDescriptor("a").description("one"), new LinkDescriptor("b").description("two"))) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void linkDescriptionFromTitleInPayload(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new LinksSnippet( - new StubLinkExtractor().withLinks(new Link("a", "alpha", "Link a"), new Link("b", "bravo", "Link b")), - Arrays.asList(new LinkDescriptor("a").description("one"), new LinkDescriptor("b"))) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`a`", "one").row("`b`", "Link b")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "links", template = "links-with-title") - void linksWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), - Arrays.asList(new LinkDescriptor("a").description("one"), new LinkDescriptor("b").description("two")), - attributes(key("title").value("Title for the links"))) - .document(operationBuilder.build()); - assertThat(snippets.links()).contains("Title for the links"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "links", template = "links-with-extra-column") - void linksWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), - Arrays.asList(new LinkDescriptor("a").description("one").attributes(key("foo").value("alpha")), - new LinkDescriptor("b").description("two").attributes(key("foo").value("bravo")))) - .document(operationBuilder.build()); - assertThat(snippets.links()).isTable((table) -> table.withHeader("Relation", "Description", "Foo") - .row("a", "one", "alpha") - .row("b", "two", "bravo")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - HypermediaDocumentation - .links(new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b", "bravo")), - new LinkDescriptor("a").description("one")) - .and(new LinkDescriptor("b").description("two")) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void tableCellContentIsEscapedWhenNecessary(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new LinksSnippet(new StubLinkExtractor().withLinks(new Link("Foo|Bar", "foo")), - Arrays.asList(new LinkDescriptor("Foo|Bar").description("one|two"))) - .document(operationBuilder.build()); - assertThat(snippets.links()) - .isTable((table) -> table.withHeader("Relation", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void undocumentedLink(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), - Collections.emptyList()) - .document(operationBuilder.build())) - .withMessage("Links with the following relations were not documented: [foo]"); - } - - @SnippetTest - void missingLink(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor(), - Arrays.asList(new LinkDescriptor("foo").description("bar"))) - .document(operationBuilder.build())) - .withMessage("Links with the following relations were not found in the response: [foo]"); - } - - @SnippetTest - void undocumentedLinkAndMissingLink(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")), - Arrays.asList(new LinkDescriptor("foo").description("bar"))) - .document(operationBuilder.build())) - .withMessage("Links with the following relations were not documented: [a]. Links with the following" - + " relations were not found in the response: [foo]"); - } - - @SnippetTest - void linkWithNoDescription(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")), - Arrays.asList(new LinkDescriptor("foo"))) - .document(operationBuilder.build())) - .withMessage("No description was provided for the link with rel 'foo' and no title was available" - + " from the link in the payload"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/StubLinkExtractor.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/StubLinkExtractor.java deleted file mode 100644 index ca5e3d6b8..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/StubLinkExtractor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.hypermedia; - -import java.io.IOException; - -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * Stub implementation of {@code LinkExtractor} for testing. - * - * @author Andy Wilkinson - */ -class StubLinkExtractor implements LinkExtractor { - - private MultiValueMap linksByRel = new LinkedMultiValueMap<>(); - - @Override - public MultiValueMap extractLinks(OperationResponse response) throws IOException { - return this.linksByRel; - } - - StubLinkExtractor withLinks(Link... links) { - for (Link link : links) { - this.linksByRel.add(link.getRel(), link); - } - return this; - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java deleted file mode 100644 index 107a4d0d4..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.net.URI; -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ContentModifyingOperationPreprocessor}. - * - * @author Andy Wilkinson - * - */ -class ContentModifyingOperationPreprocessorTests { - - private final OperationRequestFactory requestFactory = new OperationRequestFactory(); - - private final OperationResponseFactory responseFactory = new OperationResponseFactory(); - - private final ContentModifyingOperationPreprocessor preprocessor = new ContentModifyingOperationPreprocessor( - new ContentModifier() { - - @Override - public byte[] modifyContent(byte[] originalContent, MediaType mediaType) { - return "modified".getBytes(); - } - - }); - - @Test - void modifyRequestContent() { - OperationRequest request = this.requestFactory.create(URI.create("http://localhost"), HttpMethod.GET, - "content".getBytes(), new HttpHeaders(), Collections.emptyList()); - OperationRequest preprocessed = this.preprocessor.preprocess(request); - assertThat(preprocessed.getContent()).isEqualTo("modified".getBytes()); - } - - @Test - void modifyResponseContent() { - OperationResponse response = this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), - "content".getBytes()); - OperationResponse preprocessed = this.preprocessor.preprocess(response); - assertThat(preprocessed.getContent()).isEqualTo("modified".getBytes()); - } - - @Test - void contentLengthIsUpdated() { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentLength(7); - OperationRequest request = this.requestFactory.create(URI.create("http://localhost"), HttpMethod.GET, - "content".getBytes(), httpHeaders, Collections.emptyList()); - OperationRequest preprocessed = this.preprocessor.preprocess(request); - assertThat(preprocessed.getHeaders().getContentLength()).isEqualTo(8L); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java deleted file mode 100644 index a395d6402..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationRequestPreprocessorTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.operation.OperationRequest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link DelegatingOperationRequestPreprocessor}. - * - * @author Andy Wilkinson - */ -class DelegatingOperationRequestPreprocessorTests { - - @Test - void delegationOccurs() { - OperationRequest originalRequest = mock(OperationRequest.class); - OperationPreprocessor preprocessor1 = mock(OperationPreprocessor.class); - OperationRequest preprocessedRequest1 = mock(OperationRequest.class); - OperationPreprocessor preprocessor2 = mock(OperationPreprocessor.class); - OperationRequest preprocessedRequest2 = mock(OperationRequest.class); - OperationPreprocessor preprocessor3 = mock(OperationPreprocessor.class); - OperationRequest preprocessedRequest3 = mock(OperationRequest.class); - - given(preprocessor1.preprocess(originalRequest)).willReturn(preprocessedRequest1); - given(preprocessor2.preprocess(preprocessedRequest1)).willReturn(preprocessedRequest2); - given(preprocessor3.preprocess(preprocessedRequest2)).willReturn(preprocessedRequest3); - - OperationRequest result = new DelegatingOperationRequestPreprocessor( - Arrays.asList(preprocessor1, preprocessor2, preprocessor3)) - .preprocess(originalRequest); - - assertThat(result).isSameAs(preprocessedRequest3); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java deleted file mode 100644 index 513eeb869..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/DelegatingOperationResponsePreprocessorTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.operation.OperationResponse; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link DelegatingOperationResponsePreprocessor}. - * - * @author Andy Wilkinson - */ -class DelegatingOperationResponsePreprocessorTests { - - @Test - void delegationOccurs() { - OperationResponse originalResponse = mock(OperationResponse.class); - OperationPreprocessor preprocessor1 = mock(OperationPreprocessor.class); - OperationResponse preprocessedResponse1 = mock(OperationResponse.class); - OperationPreprocessor preprocessor2 = mock(OperationPreprocessor.class); - OperationResponse preprocessedResponse2 = mock(OperationResponse.class); - OperationPreprocessor preprocessor3 = mock(OperationPreprocessor.class); - OperationResponse preprocessedResponse3 = mock(OperationResponse.class); - - given(preprocessor1.preprocess(originalResponse)).willReturn(preprocessedResponse1); - given(preprocessor2.preprocess(preprocessedResponse1)).willReturn(preprocessedResponse2); - given(preprocessor3.preprocess(preprocessedResponse2)).willReturn(preprocessedResponse3); - - OperationResponse result = new DelegatingOperationResponsePreprocessor( - Arrays.asList(preprocessor1, preprocessor2, preprocessor3)) - .preprocess(originalResponse); - - assertThat(result).isSameAs(preprocessedResponse3); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java deleted file mode 100644 index b68b27ff9..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeadersModifyingOperationPreprocessorTests.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - -/** - * Tests for {@link HeadersModifyingOperationPreprocessor}. - * - * @author Jihoon Cha - * @author Andy Wilkinson - */ -class HeadersModifyingOperationPreprocessorTests { - - private final HeadersModifyingOperationPreprocessor preprocessor = new HeadersModifyingOperationPreprocessor(); - - @Test - void addNewHeader() { - this.preprocessor.add("a", "alpha"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().get("a")) - .isEqualTo(Arrays.asList("alpha")); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().get("a")) - .isEqualTo(Arrays.asList("alpha")); - } - - @Test - void addValueToExistingHeader() { - this.preprocessor.add("a", "alpha"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerSet()).contains(entry("a", Arrays.asList("apple", "alpha"))); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerSet()).contains(entry("a", Arrays.asList("apple", "alpha"))); - } - - @Test - void setNewHeader() { - this.preprocessor.set("a", "alpha", "avocado"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerSet()) - .contains(entry("a", Arrays.asList("alpha", "avocado"))); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerSet()) - .contains(entry("a", Arrays.asList("alpha", "avocado"))); - } - - @Test - void setExistingHeader() { - this.preprocessor.set("a", "alpha", "avocado"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerSet()).contains(entry("a", Arrays.asList("alpha", "avocado"))); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerSet()).contains(entry("a", Arrays.asList("alpha", "avocado"))); - } - - @Test - void removeNonExistentHeader() { - this.preprocessor.remove("a"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerNames()).doesNotContain("a"); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerNames()).doesNotContain("a"); - } - - @Test - void removeHeader() { - this.preprocessor.remove("a"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerNames()).doesNotContain("a"); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerNames()).doesNotContain("a"); - } - - @Test - void removeHeaderValueForNonExistentHeader() { - this.preprocessor.remove("a", "apple"); - assertThat(this.preprocessor.preprocess(createRequest()).getHeaders().headerNames()).doesNotContain("a"); - assertThat(this.preprocessor.preprocess(createResponse()).getHeaders().headerNames()).doesNotContain("a"); - } - - @Test - void removeHeaderValueWithMultipleValues() { - this.preprocessor.remove("a", "apple"); - assertThat( - this.preprocessor.preprocess(createRequest((headers) -> headers.addAll("a", List.of("apple", "alpha")))) - .getHeaders() - .headerSet()) - .contains(entry("a", Arrays.asList("alpha"))); - assertThat(this.preprocessor - .preprocess(createResponse((headers) -> headers.addAll("a", List.of("apple", "alpha")))) - .getHeaders() - .headerSet()).contains(entry("a", Arrays.asList("alpha"))); - } - - @Test - void removeHeaderValueWithSingleValueRemovesEntryEntirely() { - this.preprocessor.remove("a", "apple"); - assertThat(this.preprocessor.preprocess(createRequest((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerNames()).doesNotContain("a"); - assertThat(this.preprocessor.preprocess(createResponse((headers) -> headers.add("a", "apple"))) - .getHeaders() - .headerNames()).doesNotContain("a"); - } - - @Test - void removeHeadersByNamePattern() { - Consumer headersCustomizer = (headers) -> { - headers.add("apple", "apple"); - headers.add("alpha", "alpha"); - headers.add("avocado", "avocado"); - headers.add("bravo", "bravo"); - }; - this.preprocessor.removeMatching("^a.*"); - assertThat(this.preprocessor.preprocess(createRequest(headersCustomizer)).getHeaders().headerNames()) - .containsOnly("Host", "bravo"); - assertThat(this.preprocessor.preprocess(createResponse(headersCustomizer)).getHeaders().headerNames()) - .containsOnly("bravo"); - } - - private OperationRequest createRequest() { - return createRequest(null); - } - - private OperationRequest createRequest(Consumer headersCustomizer) { - HttpHeaders headers = new HttpHeaders(); - if (headersCustomizer != null) { - headersCustomizer.accept(headers); - } - return new OperationRequestFactory().create(URI.create("http://localhost:8080"), HttpMethod.GET, new byte[0], - headers, Collections.emptyList()); - } - - private OperationResponse createResponse() { - return createResponse(null); - } - - private OperationResponse createResponse(Consumer headersCustomizer) { - HttpHeaders headers = new HttpHeaders(); - if (headersCustomizer != null) { - headersCustomizer.accept(headers); - } - return new OperationResponseFactory().create(HttpStatus.OK, headers, new byte[0]); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java deleted file mode 100644 index 7fe262d13..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/LinkMaskingContentModifierTests.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.hypermedia.Link; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link LinkMaskingContentModifier}. - * - * @author Andy Wilkinson - * - */ -class LinkMaskingContentModifierTests { - - private final ContentModifier contentModifier = new LinkMaskingContentModifier(); - - private final Link[] links = new Link[] { new Link("a", "alpha"), new Link("b", "bravo") }; - - private final Link[] maskedLinks = new Link[] { new Link("a", "..."), new Link("b", "...") }; - - @Test - void halLinksAreMasked() throws Exception { - assertThat(this.contentModifier.modifyContent(halPayloadWithLinks(this.links), null)) - .isEqualTo(halPayloadWithLinks(this.maskedLinks)); - } - - @Test - void formattedHalLinksAreMasked() throws Exception { - assertThat(this.contentModifier.modifyContent(formattedHalPayloadWithLinks(this.links), null)) - .isEqualTo(formattedHalPayloadWithLinks(this.maskedLinks)); - } - - @Test - void atomLinksAreMasked() throws Exception { - assertThat(this.contentModifier.modifyContent(atomPayloadWithLinks(this.links), null)) - .isEqualTo(atomPayloadWithLinks(this.maskedLinks)); - } - - @Test - void formattedAtomLinksAreMasked() throws Exception { - assertThat(this.contentModifier.modifyContent(formattedAtomPayloadWithLinks(this.links), null)) - .isEqualTo(formattedAtomPayloadWithLinks(this.maskedLinks)); - } - - @Test - void maskCanBeCustomized() throws Exception { - assertThat( - new LinkMaskingContentModifier("custom").modifyContent(formattedAtomPayloadWithLinks(this.links), null)) - .isEqualTo(formattedAtomPayloadWithLinks(new Link("a", "custom"), new Link("b", "custom"))); - } - - @Test - void maskCanUseUtf8Characters() throws Exception { - String ellipsis = "\u2026"; - assertThat( - new LinkMaskingContentModifier(ellipsis).modifyContent(formattedHalPayloadWithLinks(this.links), null)) - .isEqualTo(formattedHalPayloadWithLinks(new Link("a", ellipsis), new Link("b", ellipsis))); - } - - private byte[] atomPayloadWithLinks(Link... links) throws JsonProcessingException { - return new ObjectMapper().writeValueAsBytes(createAtomPayload(links)); - } - - private byte[] formattedAtomPayloadWithLinks(Link... links) throws JsonProcessingException { - return new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true) - .writeValueAsBytes(createAtomPayload(links)); - } - - private AtomPayload createAtomPayload(Link... links) { - AtomPayload payload = new AtomPayload(); - payload.setLinks(Arrays.asList(links)); - return payload; - } - - private byte[] halPayloadWithLinks(Link... links) throws JsonProcessingException { - return new ObjectMapper().writeValueAsBytes(createHalPayload(links)); - } - - private byte[] formattedHalPayloadWithLinks(Link... links) throws JsonProcessingException { - return new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true) - .writeValueAsBytes(createHalPayload(links)); - } - - private HalPayload createHalPayload(Link... links) { - HalPayload payload = new HalPayload(); - Map linksMap = new LinkedHashMap<>(); - for (Link link : links) { - Map linkMap = new HashMap<>(); - linkMap.put("href", link.getHref()); - linksMap.put(link.getRel(), linkMap); - } - payload.setLinks(linksMap); - return payload; - } - - public static final class AtomPayload { - - private List links; - - public List getLinks() { - return this.links; - } - - public void setLinks(List links) { - this.links = links; - } - - } - - public static final class HalPayload { - - private Map links; - - @JsonProperty("_links") - public Map getLinks() { - return this.links; - } - - public void setLinks(Map links) { - this.links = links; - } - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java deleted file mode 100644 index 61c24b77b..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PatternReplacingContentModifierTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.regex.Pattern; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.MediaType; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link PatternReplacingContentModifier}. - * - * @author Andy Wilkinson - */ -class PatternReplacingContentModifierTests { - - @Test - void patternsAreReplaced() { - Pattern pattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", - Pattern.CASE_INSENSITIVE); - PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>"); - assertThat( - contentModifier.modifyContent("{\"id\" : \"CA761232-ED42-11CE-BACD-00AA0057B223\"}".getBytes(), null)) - .isEqualTo("{\"id\" : \"<>\"}".getBytes()); - } - - @Test - void contentThatDoesNotMatchIsUnchanged() { - Pattern pattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", - Pattern.CASE_INSENSITIVE); - PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>"); - assertThat(contentModifier.modifyContent("{\"id\" : \"CA76-ED42-11CE-BACD\"}".getBytes(), null)) - .isEqualTo("{\"id\" : \"CA76-ED42-11CE-BACD\"}".getBytes()); - } - - @Test - void encodingIsPreservedUsingCharsetFromContentType() { - String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; - Pattern pattern = Pattern.compile("[0-9]+"); - PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>"); - assertThat(contentModifier.modifyContent((japaneseContent + " 123").getBytes(), - new MediaType("text", "plain", Charset.forName("UTF-8")))) - .isEqualTo((japaneseContent + " <>").getBytes()); - } - - @Test - void encodingIsPreservedUsingFallbackCharset() { - String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4"; - Pattern pattern = Pattern.compile("[0-9]+"); - PatternReplacingContentModifier contentModifier = new PatternReplacingContentModifier(pattern, "<>", - StandardCharsets.UTF_8); - assertThat(contentModifier.modifyContent((japaneseContent + " 123").getBytes(), new MediaType("text", "plain"))) - .isEqualTo((japaneseContent + " <>").getBytes()); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java deleted file mode 100644 index a0c28376a..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/PrettyPrintingContentModifierTests.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.testfixtures.jupiter.CapturedOutput; -import org.springframework.restdocs.testfixtures.jupiter.OutputCaptureExtension; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link PrettyPrintingContentModifier}. - * - * @author Andy Wilkinson - * - */ -@ExtendWith(OutputCaptureExtension.class) -class PrettyPrintingContentModifierTests { - - @Test - void prettyPrintJson() { - assertThat(new PrettyPrintingContentModifier().modifyContent("{\"a\":5}".getBytes(), null)) - .isEqualTo(String.format("{%n \"a\" : 5%n}").getBytes()); - } - - @Test - void prettyPrintXml() { - assertThat(new PrettyPrintingContentModifier() - .modifyContent("".getBytes(), null)) - .isEqualTo(String - .format("%n" - + "%n %n%n") - .getBytes()); - } - - @Test - void empytContentIsHandledGracefully() { - assertThat(new PrettyPrintingContentModifier().modifyContent("".getBytes(), null)).isEqualTo("".getBytes()); - } - - @Test - void nonJsonAndNonXmlContentIsHandledGracefully(CapturedOutput output) { - String content = "abcdefg"; - assertThat(new PrettyPrintingContentModifier().modifyContent(content.getBytes(), null)) - .isEqualTo(content.getBytes()); - assertThat(output).isEmpty(); - } - - @Test - void nonJsonContentThatInitiallyLooksLikeJsonIsHandledGracefully(CapturedOutput output) { - String content = "\"abc\",\"def\""; - assertThat(new PrettyPrintingContentModifier().modifyContent(content.getBytes(), null)) - .isEqualTo(content.getBytes()); - assertThat(output).isEmpty(); - } - - @Test - void encodingIsPreserved() throws Exception { - Map input = new HashMap<>(); - input.put("japanese", "\u30b3\u30f3\u30c6\u30f3\u30c4"); - ObjectMapper objectMapper = new ObjectMapper(); - @SuppressWarnings("unchecked") - Map output = objectMapper.readValue( - new PrettyPrintingContentModifier().modifyContent(objectMapper.writeValueAsBytes(input), null), - Map.class); - assertThat(output).isEqualTo(input); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java deleted file mode 100644 index 0c5724eb7..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/UriModifyingOperationPreprocessorTests.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.operation.preprocess; - -import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationRequestPartFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.RequestCookie; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link UriModifyingOperationPreprocessor}. - * - * @author Andy Wilkinson - */ -class UriModifyingOperationPreprocessorTests { - - private final OperationRequestFactory requestFactory = new OperationRequestFactory(); - - private final OperationResponseFactory responseFactory = new OperationResponseFactory(); - - private final UriModifyingOperationPreprocessor preprocessor = new UriModifyingOperationPreprocessor(); - - @Test - void requestUriSchemeCanBeModified() { - this.preprocessor.scheme("https"); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithUri("http://localhost:12345")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://localhost:12345")); - } - - @Test - void requestUriHostCanBeModified() { - this.preprocessor.host("api.example.com"); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithUri("https://api.foo.com:12345")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com:12345")); - assertThat(processed.getHeaders().getFirst(HttpHeaders.HOST)).isEqualTo("api.example.com:12345"); - } - - @Test - void requestUriPortCanBeModified() { - this.preprocessor.port(23456); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithUri("https://api.example.com:12345")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com:23456")); - assertThat(processed.getHeaders().getFirst(HttpHeaders.HOST)).isEqualTo("api.example.com:23456"); - } - - @Test - void requestUriPortCanBeRemoved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithUri("https://api.example.com:12345")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com")); - assertThat(processed.getHeaders().getFirst(HttpHeaders.HOST)).isEqualTo("api.example.com"); - } - - @Test - void requestUriPathIsPreserved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithUri("https://api.example.com:12345/foo/bar")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com/foo/bar")); - } - - @Test - void requestUriQueryIsPreserved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithUri("https://api.example.com:12345?foo=bar")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com?foo=bar")); - } - - @Test - void requestUriAnchorIsPreserved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithUri("https://api.example.com:12345#foo")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://api.example.com#foo")); - } - - @Test - void requestContentUriSchemeCanBeModified() { - this.preprocessor.scheme("https"); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( - "The uri 'https://localhost:12345' should be used. foo:bar will be unaffected")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'https://localhost:12345' should be used. foo:bar will be unaffected"); - } - - @Test - void requestContentUriHostCanBeModified() { - this.preprocessor.host("api.example.com"); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( - "The uri 'https://localhost:12345' should be used. foo:bar will be unaffected")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'https://api.example.com:12345' should be used. foo:bar will be unaffected"); - } - - @Test - void requestContentHostOfUriWithoutPortCanBeModified() { - this.preprocessor.host("api.example.com"); - OperationRequest processed = this.preprocessor.preprocess( - createRequestWithContent("The uri 'https://localhost' should be used. foo:bar will be unaffected")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'https://api.example.com' should be used. foo:bar will be unaffected"); - } - - @Test - void requestContentUriPortCanBeAdded() { - this.preprocessor.port(23456); - OperationRequest processed = this.preprocessor.preprocess( - createRequestWithContent("The uri 'http://localhost' should be used. foo:bar will be unaffected")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'http://localhost:23456' should be used. foo:bar will be unaffected"); - } - - @Test - void requestContentUriPortCanBeModified() { - this.preprocessor.port(23456); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( - "The uri 'http://localhost:12345' should be used. foo:bar will be unaffected")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'http://localhost:23456' should be used. foo:bar will be unaffected"); - } - - @Test - void requestContentUriPortCanBeRemoved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( - "The uri 'http://localhost:12345' should be used. foo:bar will be unaffected")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'http://localhost' should be used. foo:bar will be unaffected"); - } - - @Test - void multipleRequestContentUrisCanBeModified() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor.preprocess(createRequestWithContent( - "Use 'http://localhost:12345' or 'https://localhost:23456' to access the service")); - assertThat(new String(processed.getContent())) - .isEqualTo("Use 'http://localhost' or 'https://localhost' to access the service"); - } - - @Test - void requestContentUriPathIsPreserved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithContent("The uri 'http://localhost:12345/foo/bar' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost/foo/bar' should be used"); - } - - @Test - void requestContentUriQueryIsPreserved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithContent("The uri 'http://localhost:12345?foo=bar' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost?foo=bar' should be used"); - } - - @Test - void requestContentUriAnchorIsPreserved() { - this.preprocessor.removePort(); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithContent("The uri 'http://localhost:12345#foo' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost#foo' should be used"); - } - - @Test - void responseContentUriSchemeCanBeModified() { - this.preprocessor.scheme("https"); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'http://localhost:12345' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'https://localhost:12345' should be used"); - } - - @Test - void responseContentUriHostCanBeModified() { - this.preprocessor.host("api.example.com"); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'https://localhost:12345' should be used")); - assertThat(new String(processed.getContent())) - .isEqualTo("The uri 'https://api.example.com:12345' should be used"); - } - - @Test - void responseContentUriPortCanBeModified() { - this.preprocessor.port(23456); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'http://localhost:12345' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost:23456' should be used"); - } - - @Test - void responseContentUriPortCanBeRemoved() { - this.preprocessor.removePort(); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'http://localhost:12345' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost' should be used"); - } - - @Test - void multipleResponseContentUrisCanBeModified() { - this.preprocessor.removePort(); - OperationResponse processed = this.preprocessor.preprocess(createResponseWithContent( - "Use 'http://localhost:12345' or 'https://localhost:23456' to access the service")); - assertThat(new String(processed.getContent())) - .isEqualTo("Use 'http://localhost' or 'https://localhost' to access the service"); - } - - @Test - void responseContentUriPathIsPreserved() { - this.preprocessor.removePort(); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'http://localhost:12345/foo/bar' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost/foo/bar' should be used"); - } - - @Test - void responseContentUriQueryIsPreserved() { - this.preprocessor.removePort(); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'http://localhost:12345?foo=bar' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost?foo=bar' should be used"); - } - - @Test - void responseContentUriAnchorIsPreserved() { - this.preprocessor.removePort(); - OperationResponse processed = this.preprocessor - .preprocess(createResponseWithContent("The uri 'http://localhost:12345#foo' should be used")); - assertThat(new String(processed.getContent())).isEqualTo("The uri 'http://localhost#foo' should be used"); - } - - @Test - void urisInRequestHeadersCanBeModified() { - OperationRequest processed = this.preprocessor.host("api.example.com") - .preprocess(createRequestWithHeader("Foo", "https://locahost:12345")); - assertThat(processed.getHeaders().getFirst("Foo")).isEqualTo("https://api.example.com:12345"); - assertThat(processed.getHeaders().getFirst("Host")).isEqualTo("api.example.com"); - } - - @Test - void urisInResponseHeadersCanBeModified() { - OperationResponse processed = this.preprocessor.host("api.example.com") - .preprocess(createResponseWithHeader("Foo", "https://locahost:12345")); - assertThat(processed.getHeaders().getFirst("Foo")).isEqualTo("https://api.example.com:12345"); - } - - @Test - void urisInRequestPartHeadersCanBeModified() { - OperationRequest processed = this.preprocessor.host("api.example.com") - .preprocess(createRequestWithPartWithHeader("Foo", "https://locahost:12345")); - assertThat(processed.getParts().iterator().next().getHeaders().getFirst("Foo")) - .isEqualTo("https://api.example.com:12345"); - } - - @Test - void urisInRequestPartContentCanBeModified() { - OperationRequest processed = this.preprocessor.host("api.example.com") - .preprocess(createRequestWithPartWithContent("The uri 'https://localhost:12345' should be used")); - assertThat(new String(processed.getParts().iterator().next().getContent())) - .isEqualTo("The uri 'https://api.example.com:12345' should be used"); - } - - @Test - void modifiedUriDoesNotGetDoubleEncoded() { - this.preprocessor.scheme("https"); - OperationRequest processed = this.preprocessor - .preprocess(createRequestWithUri("http://localhost:12345?foo=%7B%7D")); - assertThat(processed.getUri()).isEqualTo(URI.create("https://localhost:12345?foo=%7B%7D")); - - } - - @Test - void resultingRequestHasCookiesFromOriginalRequst() { - List cookies = Arrays.asList(new RequestCookie("a", "alpha")); - OperationRequest request = this.requestFactory.create(URI.create("http://localhost:12345"), HttpMethod.GET, - new byte[0], new HttpHeaders(), Collections.emptyList(), cookies); - OperationRequest processed = this.preprocessor.preprocess(request); - assertThat(processed.getCookies().size()).isEqualTo(1); - } - - private OperationRequest createRequestWithUri(String uri) { - return this.requestFactory.create(URI.create(uri), HttpMethod.GET, new byte[0], new HttpHeaders(), - Collections.emptyList()); - } - - private OperationRequest createRequestWithContent(String content) { - return this.requestFactory.create(URI.create("http://localhost"), HttpMethod.GET, content.getBytes(), - new HttpHeaders(), Collections.emptyList()); - } - - private OperationRequest createRequestWithHeader(String name, String value) { - HttpHeaders headers = new HttpHeaders(); - headers.add(name, value); - return this.requestFactory.create(URI.create("http://localhost"), HttpMethod.GET, new byte[0], headers, - Collections.emptyList()); - } - - private OperationRequest createRequestWithPartWithHeader(String name, String value) { - HttpHeaders headers = new HttpHeaders(); - headers.add(name, value); - return this.requestFactory.create(URI.create("http://localhost"), HttpMethod.GET, new byte[0], - new HttpHeaders(), - Arrays.asList(new OperationRequestPartFactory().create("part", "fileName", new byte[0], headers))); - } - - private OperationRequest createRequestWithPartWithContent(String content) { - return this.requestFactory.create(URI.create("http://localhost"), HttpMethod.GET, new byte[0], - new HttpHeaders(), Arrays.asList(new OperationRequestPartFactory().create("part", "fileName", - content.getBytes(), new HttpHeaders()))); - } - - private OperationResponse createResponseWithContent(String content) { - return this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), content.getBytes()); - } - - private OperationResponse createResponseWithHeader(String name, String value) { - HttpHeaders headers = new HttpHeaders(); - headers.add(name, value); - return this.responseFactory.create(HttpStatus.OK, headers, new byte[0]); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java deleted file mode 100644 index ce0945462..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/AsciidoctorRequestFieldsSnippetTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Arrays; - -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest.Format; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; - -/** - * Tests for {@link RequestFieldsSnippet} that are specific to Asciidoctor. - * - * @author Andy Wilkinson - */ -class AsciidoctorRequestFieldsSnippetTests { - - @RenderedSnippetTest(format = Format.ASCIIDOCTOR) - @SnippetTemplate(snippet = "request-fields", template = "request-fields-with-list-description") - void requestFieldsWithListDescription(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description(Arrays.asList("one", "two")))) - .document(operationBuilder.request("http://localhost").content("{\"a\": \"foo\"}").build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("a", "String", String.format(" - one%n - two")) - .configuration("[cols=\"1,1,1a\"]")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java deleted file mode 100644 index a5d0b958c..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldPathPayloadSubsectionExtractorTests.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import org.junit.jupiter.api.Test; - -import org.springframework.http.MediaType; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Tests for {@link FieldPathPayloadSubsectionExtractor}. - * - * @author Andy Wilkinson - */ -class FieldPathPayloadSubsectionExtractorTests { - - @Test - @SuppressWarnings("unchecked") - void extractMapSubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.b") - .extractSubsection("{\"a\":{\"b\":{\"c\":5}}}".getBytes(), MediaType.APPLICATION_JSON); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted.get("c")).isEqualTo(5); - } - - @Test - @SuppressWarnings("unchecked") - void extractSingleElementArraySubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[]") - .extractSubsection("{\"a\":[{\"b\":5}]}".getBytes(), MediaType.APPLICATION_JSON); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted).containsOnlyKeys("b"); - } - - @Test - @SuppressWarnings("unchecked") - void extractMultiElementArraySubsectionOfJsonMap() throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a") - .extractSubsection("{\"a\":[{\"b\":5},{\"b\":4}]}".getBytes(), MediaType.APPLICATION_JSON); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted).containsOnlyKeys("b"); - } - - @Test - @SuppressWarnings("unchecked") - void extractMapSubsectionFromSingleElementArrayInAJsonMap() - throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b") - .extractSubsection("{\"a\":[{\"b\":{\"c\":5}}]}".getBytes(), MediaType.APPLICATION_JSON); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted.get("c")).isEqualTo(5); - } - - @Test - @SuppressWarnings("unchecked") - void extractMapSubsectionWithCommonStructureFromMultiElementArrayInAJsonMap() - throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b") - .extractSubsection("{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6}}]}".getBytes(), MediaType.APPLICATION_JSON); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted).containsOnlyKeys("c"); - } - - @Test - void extractMapSubsectionWithVaryingStructureFromMultiElementArrayInAJsonMap() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new FieldPathPayloadSubsectionExtractor("a.[].b").extractSubsection( - "{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6, \"d\": 7}}]}".getBytes(), MediaType.APPLICATION_JSON)) - .withMessageContaining("The following non-optional uncommon paths were found: [a.[].b.d]"); - } - - @Test - void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMap() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new FieldPathPayloadSubsectionExtractor("*.d").extractSubsection( - "{\"a\":{\"b\":1},\"c\":{\"d\":{\"e\":1,\"f\":2}}}".getBytes(), MediaType.APPLICATION_JSON)) - .withMessageContaining("The following non-optional uncommon paths were found: [*.d, *.d.e, *.d.f]"); - } - - @Test - void extractMapSubsectionWithVaryingStructureFromInconsistentJsonMapWhereAllSubsectionFieldsAreOptional() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new FieldPathPayloadSubsectionExtractor("*.d").extractSubsection( - "{\"a\":{\"b\":1},\"c\":{\"d\":{\"e\":1,\"f\":2}}}".getBytes(), MediaType.APPLICATION_JSON, - Arrays.asList(new FieldDescriptor("e").optional(), new FieldDescriptor("f").optional()))) - .withMessageContaining("The following non-optional uncommon paths were found: [*.d]"); - } - - @Test - @SuppressWarnings("unchecked") - void extractMapSubsectionWithVaryingStructureDueToOptionalFieldsFromMultiElementArrayInAJsonMap() - throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b").extractSubsection( - "{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6, \"d\": 7}}]}".getBytes(), MediaType.APPLICATION_JSON, - Arrays.asList(new FieldDescriptor("d").optional())); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted).containsOnlyKeys("c"); - } - - @Test - @SuppressWarnings("unchecked") - void extractMapSubsectionWithVaryingStructureDueToOptionalParentFieldsFromMultiElementArrayInAJsonMap() - throws JsonParseException, JsonMappingException, IOException { - byte[] extractedPayload = new FieldPathPayloadSubsectionExtractor("a.[].b").extractSubsection( - "{\"a\":[{\"b\":{\"c\":5}},{\"b\":{\"c\":6, \"d\": { \"e\": 7}}}]}".getBytes(), - MediaType.APPLICATION_JSON, Arrays.asList(new FieldDescriptor("d").optional())); - Map extracted = new ObjectMapper().readValue(extractedPayload, Map.class); - assertThat(extracted.size()).isEqualTo(1); - assertThat(extracted).containsOnlyKeys("c"); - } - - @Test - void extractedSubsectionIsPrettyPrintedWhenInputIsPrettyPrinted() - throws JsonParseException, JsonMappingException, JsonProcessingException, IOException { - ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - byte[] prettyPrintedPayload = objectMapper - .writeValueAsBytes(objectMapper.readValue("{\"a\": { \"b\": { \"c\": 1 }}}", Object.class)); - byte[] extractedSubsection = new FieldPathPayloadSubsectionExtractor("a.b") - .extractSubsection(prettyPrintedPayload, MediaType.APPLICATION_JSON); - byte[] prettyPrintedSubsection = objectMapper - .writeValueAsBytes(objectMapper.readValue("{\"c\": 1 }", Object.class)); - assertThat(new String(extractedSubsection)).isEqualTo(new String(prettyPrintedSubsection)); - } - - @Test - void extractedSubsectionIsNotPrettyPrintedWhenInputIsNotPrettyPrinted() - throws JsonParseException, JsonMappingException, JsonProcessingException, IOException { - ObjectMapper objectMapper = new ObjectMapper(); - byte[] payload = objectMapper - .writeValueAsBytes(objectMapper.readValue("{\"a\": { \"b\": { \"c\": 1 }}}", Object.class)); - byte[] extractedSubsection = new FieldPathPayloadSubsectionExtractor("a.b").extractSubsection(payload, - MediaType.APPLICATION_JSON); - byte[] subsection = objectMapper.writeValueAsBytes(objectMapper.readValue("{\"c\": 1 }", Object.class)); - assertThat(new String(extractedSubsection)).isEqualTo(new String(subsection)); - } - - @Test - void extractNonExistentSubsection() { - assertThatThrownBy(() -> new FieldPathPayloadSubsectionExtractor("a.c") - .extractSubsection("{\"a\":{\"b\":{\"c\":5}}}".getBytes(), MediaType.APPLICATION_JSON)) - .isInstanceOf(PayloadHandlingException.class) - .hasMessage("a.c does not identify a section of the payload"); - } - - @Test - void extractEmptyArraySubsection() { - assertThatThrownBy(() -> new FieldPathPayloadSubsectionExtractor("a") - .extractSubsection("{\"a\":[]}}".getBytes(), MediaType.APPLICATION_JSON)) - .isInstanceOf(PayloadHandlingException.class) - .hasMessage("a identifies an empty section of the payload"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java deleted file mode 100644 index da5b1e902..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/FieldTypeResolverTests.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.MediaType; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for {@link FieldTypeResolver}. - * - * @author Mathias Düsterhöft - */ -public class FieldTypeResolverTests { - - @Test - public void whenForContentWithDescriptorsCalledWithJsonContentThenReturnsJsonFieldTypeResolver() { - assertThat(FieldTypeResolver.forContentWithDescriptors("{\"field\": \"value\"}".getBytes(), - MediaType.APPLICATION_JSON, Collections.emptyList())) - .isInstanceOf(JsonContentHandler.class); - } - - @Test - public void whenForContentWithDescriptorsCalledWithXmlContentThenReturnsXmlContentHandler() { - assertThat(FieldTypeResolver.forContentWithDescriptors("5".getBytes(), MediaType.APPLICATION_XML, - Collections.emptyList())) - .isInstanceOf(XmlContentHandler.class); - } - - @Test - public void whenForContentWithDescriptorsIsCalledWithInvalidContentThenExceptionIsThrown() { - assertThatExceptionOfType(PayloadHandlingException.class).isThrownBy(() -> FieldTypeResolver - .forContentWithDescriptors("some".getBytes(), MediaType.APPLICATION_XML, Collections.emptyList())); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java deleted file mode 100644 index 43f0100af..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonContentHandlerTests.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for {@link JsonContentHandler}. - * - * @author Andy Wilkinson - * @author Mathias Düsterhöft - */ -class JsonContentHandlerTests { - - @Test - void typeForFieldWithNullValueMustMatch() { - FieldDescriptor descriptor = new FieldDescriptor("a").type(JsonFieldType.STRING); - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new JsonContentHandler("{\"a\": null}".getBytes(), Arrays.asList(descriptor)) - .resolveFieldType(descriptor)); - } - - @Test - void typeForFieldWithNotNullAndThenNullValueMustMatch() { - FieldDescriptor descriptor = new FieldDescriptor("a[].id").type(JsonFieldType.STRING); - assertThatExceptionOfType(FieldTypesDoNotMatchException.class).isThrownBy( - () -> new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}".getBytes(), Arrays.asList(descriptor)) - .resolveFieldType(descriptor)); - } - - @Test - void typeForFieldWithNullAndThenNotNullValueMustMatch() { - FieldDescriptor descriptor = new FieldDescriptor("a.[].id").type(JsonFieldType.STRING); - assertThatExceptionOfType(FieldTypesDoNotMatchException.class).isThrownBy( - () -> new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), Arrays.asList(descriptor)) - .resolveFieldType(descriptor)); - } - - @Test - void typeForOptionalFieldWithNumberAndThenNullValueIsNumber() { - FieldDescriptor descriptor = new FieldDescriptor("a[].id").optional(); - Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes(), - Arrays.asList(descriptor)) - .resolveFieldType(descriptor); - assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER); - } - - @Test - void typeForOptionalFieldWithNullAndThenNumberIsNumber() { - FieldDescriptor descriptor = new FieldDescriptor("a[].id").optional(); - Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), - Arrays.asList(descriptor)) - .resolveFieldType(descriptor); - assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER); - } - - @Test - void typeForFieldWithNumberAndThenNullValueIsVaries() { - FieldDescriptor descriptor = new FieldDescriptor("a[].id"); - Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":1},{\"id\":null}]}\"".getBytes(), - Arrays.asList(descriptor)) - .resolveFieldType(descriptor); - assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES); - } - - @Test - void typeForFieldWithNullAndThenNumberIsVaries() { - FieldDescriptor descriptor = new FieldDescriptor("a[].id"); - Object fieldType = new JsonContentHandler("{\"a\":[{\"id\":null},{\"id\":1}]}".getBytes(), - Arrays.asList(descriptor)) - .resolveFieldType(descriptor); - assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.VARIES); - } - - @Test - void typeForOptionalFieldWithNullValueCanBeProvidedExplicitly() { - FieldDescriptor descriptor = new FieldDescriptor("a").type(JsonFieldType.STRING).optional(); - Object fieldType = new JsonContentHandler("{\"a\": null}".getBytes(), Arrays.asList(descriptor)) - .resolveFieldType(descriptor); - assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.STRING); - } - - @Test - void typeForFieldWithSometimesPresentOptionalAncestorCanBeProvidedExplicitly() { - FieldDescriptor descriptor = new FieldDescriptor("a.[].b.c").type(JsonFieldType.NUMBER); - FieldDescriptor ancestor = new FieldDescriptor("a.[].b").optional(); - Object fieldType = new JsonContentHandler("{\"a\":[ { \"d\": 4}, {\"b\":{\"c\":5}, \"d\": 4}]}".getBytes(), - Arrays.asList(descriptor, ancestor)) - .resolveFieldType(descriptor); - assertThat((JsonFieldType) fieldType).isEqualTo(JsonFieldType.NUMBER); - } - - @Test - void failsFastWithNonJsonContent() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new JsonContentHandler("Non-JSON content".getBytes(), Collections.emptyList())); - } - - @Test - void describedFieldThatIsNotPresentIsConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"), - new FieldDescriptor("c")); - List missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes(), - descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(1); - assertThat(missingFields.get(0).getPath()).isEqualTo("c"); - } - - @Test - void describedOptionalFieldThatIsNotPresentIsNotConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a"), new FieldDescriptor("b"), - new FieldDescriptor("c").optional()); - List missingFields = new JsonContentHandler("{\"a\": \"alpha\", \"b\":\"bravo\"}".getBytes(), - descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(0); - } - - @Test - void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsPresentIsConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"), - new FieldDescriptor("a.c")); - List missingFields = new JsonContentHandler("{\"a\":\"alpha\",\"b\":\"bravo\"}".getBytes(), - descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(1); - assertThat(missingFields.get(0).getPath()).isEqualTo("a.c"); - } - - @Test - void describedFieldThatIsNotPresentNestedBeneathOptionalFieldThatIsNotPresentIsNotConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a").optional(), new FieldDescriptor("b"), - new FieldDescriptor("a.c")); - List missingFields = new JsonContentHandler("{\"b\":\"bravo\"}".getBytes(), descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(0); - } - - @Test - void describedFieldThatIsNotPresentNestedBeneathOptionalArrayThatIsEmptyIsNotConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("outer"), - new FieldDescriptor("outer[]").optional(), new FieldDescriptor("outer[].inner")); - List missingFields = new JsonContentHandler("{\"outer\":[]}".getBytes(), descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(0); - } - - @Test - void describedSometimesPresentFieldThatIsChildOfSometimesPresentOptionalArrayIsNotConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a.[].c").optional(), - new FieldDescriptor("a.[].c.d")); - List missingFields = new JsonContentHandler( - "{\"a\":[ {\"b\": \"bravo\"}, {\"b\": \"bravo\", \"c\": { \"d\": \"delta\"}}]}".getBytes(), descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(0); - } - - @Test - void describedMissingFieldThatIsChildOfNestedOptionalArrayThatIsEmptyIsNotConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a.[].b").optional(), - new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c")); - List missingFields = new JsonContentHandler("{\"a\":[{\"b\":[]}]}".getBytes(), descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(0); - } - - @Test - void describedMissingFieldThatIsChildOfNestedOptionalArrayThatContainsAnObjectIsConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a.[].b").optional(), - new FieldDescriptor("a.[].b.[]").optional(), new FieldDescriptor("a.[].b.[].c")); - List missingFields = new JsonContentHandler("{\"a\":[{\"b\":[{}]}]}".getBytes(), descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(1); - assertThat(missingFields.get(0).getPath()).isEqualTo("a.[].b.[].c"); - } - - @Test - void describedMissingFieldThatIsChildOfOptionalObjectThatIsNullIsNotConsideredMissing() { - List descriptors = Arrays.asList(new FieldDescriptor("a").optional(), - new FieldDescriptor("a.b")); - List missingFields = new JsonContentHandler("{\"a\":null}".getBytes(), descriptors) - .findMissingFields(); - assertThat(missingFields.size()).isEqualTo(0); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java deleted file mode 100644 index 69324c659..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathTests.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.payload.JsonFieldPath.PathType; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link JsonFieldPath}. - * - * @author Andy Wilkinson - * @author Jeremy Rickard - */ -class JsonFieldPathTests { - - @Test - void pathTypeOfSingleFieldIsSingle() { - JsonFieldPath path = JsonFieldPath.compile("a"); - assertThat(path.getType()).isEqualTo(PathType.SINGLE); - } - - @Test - void pathTypeOfSingleNestedFieldIsSingle() { - JsonFieldPath path = JsonFieldPath.compile("a.b"); - assertThat(path.getType()).isEqualTo(PathType.SINGLE); - } - - @Test - void pathTypeOfTopLevelArrayIsSingle() { - JsonFieldPath path = JsonFieldPath.compile("[]"); - assertThat(path.getType()).isEqualTo(PathType.SINGLE); - } - - @Test - void pathTypeOfFieldBeneathTopLevelArrayIsMulti() { - JsonFieldPath path = JsonFieldPath.compile("[]a"); - assertThat(path.getType()).isEqualTo(PathType.MULTI); - } - - @Test - void pathTypeOfSingleNestedArrayIsSingle() { - JsonFieldPath path = JsonFieldPath.compile("a[]"); - assertThat(path.getType()).isEqualTo(PathType.SINGLE); - } - - @Test - void pathTypeOfArrayBeneathNestedFieldsIsSingle() { - JsonFieldPath path = JsonFieldPath.compile("a.b[]"); - assertThat(path.getType()).isEqualTo(PathType.SINGLE); - } - - @Test - void pathTypeOfArrayOfArraysIsMulti() { - JsonFieldPath path = JsonFieldPath.compile("a[][]"); - assertThat(path.getType()).isEqualTo(PathType.MULTI); - } - - @Test - void pathTypeOfFieldBeneathAnArrayIsMulti() { - JsonFieldPath path = JsonFieldPath.compile("a[].b"); - assertThat(path.getType()).isEqualTo(PathType.MULTI); - } - - @Test - void pathTypeOfFieldBeneathTopLevelWildcardIsMulti() { - JsonFieldPath path = JsonFieldPath.compile("*.a"); - assertThat(path.getType()).isEqualTo(PathType.MULTI); - } - - @Test - void pathTypeOfFieldBeneathNestedWildcardIsMulti() { - JsonFieldPath path = JsonFieldPath.compile("a.*.b"); - assertThat(path.getType()).isEqualTo(PathType.MULTI); - } - - @Test - void pathTypeOfLeafWidlcardIsMulti() { - JsonFieldPath path = JsonFieldPath.compile("a.*"); - assertThat(path.getType()).isEqualTo(PathType.MULTI); - } - - @Test - void compilationOfSingleElementPath() { - assertThat(JsonFieldPath.compile("a").getSegments()).containsExactly("a"); - } - - @Test - void compilationOfMultipleElementPath() { - assertThat(JsonFieldPath.compile("a.b.c").getSegments()).containsExactly("a", "b", "c"); - } - - @Test - void compilationOfPathWithArraysWithNoDotSeparators() { - assertThat(JsonFieldPath.compile("a[]b[]c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); - } - - @Test - void compilationOfPathWithArraysWithPreAndPostDotSeparators() { - assertThat(JsonFieldPath.compile("a.[].b.[].c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); - } - - @Test - void compilationOfPathWithArraysWithPreDotSeparators() { - assertThat(JsonFieldPath.compile("a.[]b.[]c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); - } - - @Test - void compilationOfPathWithArraysWithPostDotSeparators() { - assertThat(JsonFieldPath.compile("a[].b[].c").getSegments()).containsExactly("a", "[]", "b", "[]", "c"); - } - - @Test - void compilationOfPathStartingWithAnArray() { - assertThat(JsonFieldPath.compile("[]a.b.c").getSegments()).containsExactly("[]", "a", "b", "c"); - } - - @Test - void compilationOfMultipleElementPathWithBrackets() { - assertThat(JsonFieldPath.compile("['a']['b']['c']").getSegments()).containsExactly("a", "b", "c"); - } - - @Test - void compilationOfMultipleElementPathWithAndWithoutBrackets() { - assertThat(JsonFieldPath.compile("['a'][].b['c']").getSegments()).containsExactly("a", "[]", "b", "c"); - } - - @Test - void compilationOfMultipleElementPathWithAndWithoutBracketsAndEmbeddedDots() { - assertThat(JsonFieldPath.compile("['a.key'][].b['c']").getSegments()).containsExactly("a.key", "[]", "b", "c"); - } - - @Test - void compilationOfPathWithAWildcard() { - assertThat(JsonFieldPath.compile("a.b.*.c").getSegments()).containsExactly("a", "b", "*", "c"); - } - - @Test - void compilationOfPathWithAWildcardInBrackets() { - assertThat(JsonFieldPath.compile("a.b.['*'].c").getSegments()).containsExactly("a", "b", "*", "c"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java deleted file mode 100644 index 2e770649c..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldPathsTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Arrays; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link JsonFieldPaths}. - * - * @author Andy Wilkinson - */ -class JsonFieldPathsTests { - - @Test - void noUncommonPathsForSingleItem() { - assertThat( - JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": [ { \"c\": 2}, {\"c\": 3}, {\"c\": null}]}"))) - .getUncommon()) - .isEmpty(); - } - - @Test - void noUncommonPathsForMultipleIdenticalItems() { - Object item = json("{\"a\": 1, \"b\": [ { \"c\": 2}, {\"c\": 3} ]}"); - assertThat(JsonFieldPaths.from(Arrays.asList(item, item)).getUncommon()).isEmpty(); - } - - @Test - void noUncommonPathsForMultipleMatchingItemsWithDifferentScalarValues() { - assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": [ { \"c\": 2}, {\"c\": 3} ]}"), - json("{\"a\": 4, \"b\": [ { \"c\": 5}, {\"c\": 6} ]}"))) - .getUncommon()).isEmpty(); - } - - @Test - void missingEntryInMapIsIdentifiedAsUncommon() { - assertThat( - JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1}"), json("{\"a\": 1}"), json("{\"a\": 1, \"b\": 2}"))) - .getUncommon()) - .containsExactly("b"); - } - - @Test - void missingEntryInNestedMapIsIdentifiedAsUncommon() { - assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": {\"c\": 1}}"), - json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"c\": 1, \"d\": 2}}"))) - .getUncommon()).containsExactly("b.d"); - } - - @Test - void missingEntriesInNestedMapAreIdentifiedAsUncommon() { - assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": 1, \"b\": {\"c\": 1}}"), - json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"d\": 2}}"))) - .getUncommon()).containsExactly("b.c", "b.d"); - } - - @Test - void absentItemFromFieldExtractionCausesAllPresentFieldsToBeIdentifiedAsUncommon() { - assertThat(JsonFieldPaths - .from(Arrays.asList(ExtractedField.ABSENT, json("{\"a\": 1, \"b\": {\"c\": 1}}"), - json("{\"a\": 1, \"b\": {\"c\": 1}}"), json("{\"a\": 1, \"b\": {\"d\": 2}}"))) - .getUncommon()).containsExactly("", "a", "b", "b.c", "b.d"); - } - - @Test - void missingEntryBeneathArrayIsIdentifiedAsUncommon() { - assertThat(JsonFieldPaths - .from(Arrays.asList(json("[{\"b\": 1}]"), json("[{\"b\": 1}]"), json("[{\"b\": 1, \"c\": 2}]"))) - .getUncommon()).containsExactly("[].c"); - } - - @Test - void missingEntryBeneathNestedArrayIsIdentifiedAsUncommon() { - assertThat(JsonFieldPaths.from(Arrays.asList(json("{\"a\": [{\"b\": 1}]}"), json("{\"a\": [{\"b\": 1}]}"), - json("{\"a\": [{\"b\": 1, \"c\": 2}]}"))) - .getUncommon()).containsExactly("a.[].c"); - } - - private Object json(String json) { - try { - return new ObjectMapper().readValue(json, Object.class); - } - catch (IOException ex) { - throw new RuntimeException(ex); - } - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java deleted file mode 100644 index b87099857..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldProcessorTests.java +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.payload.JsonFieldProcessor.ExtractedField; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link JsonFieldProcessor}. - * - * @author Andy Wilkinson - */ -class JsonFieldProcessorTests { - - private final JsonFieldProcessor fieldProcessor = new JsonFieldProcessor(); - - @Test - void extractTopLevelMapEntry() { - Map payload = new HashMap<>(); - payload.put("a", "alpha"); - assertThat(this.fieldProcessor.extract("a", payload).getValue()).isEqualTo("alpha"); - } - - @Test - void extractNestedMapEntry() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo"); - assertThat(this.fieldProcessor.extract("a.b", payload).getValue()).isEqualTo("bravo"); - } - - @Test - void extractTopLevelArray() { - List> payload = new ArrayList<>(); - Map bravo = new HashMap<>(); - bravo.put("b", "bravo"); - payload.add(bravo); - payload.add(bravo); - assertThat(this.fieldProcessor.extract("[]", payload).getValue()).isEqualTo(payload); - } - - @Test - void extractArray() { - Map payload = new HashMap<>(); - Map bravo = new HashMap<>(); - bravo.put("b", "bravo"); - List> alpha = Arrays.asList(bravo, bravo); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a", payload).getValue()).isEqualTo(alpha); - } - - @Test - void extractArrayContents() { - Map payload = new HashMap<>(); - Map bravo = new HashMap<>(); - bravo.put("b", "bravo"); - List> alpha = Arrays.asList(bravo, bravo); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[]", payload).getValue()).isEqualTo(alpha); - } - - @Test - void extractFromItemsInArray() { - Map payload = new HashMap<>(); - Map entry = new HashMap<>(); - entry.put("b", "bravo"); - List> alpha = Arrays.asList(entry, entry); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[].b", payload).getValue()).isEqualTo(Arrays.asList("bravo", "bravo")); - } - - @Test - void extractOccasionallyAbsentFieldFromItemsInArray() { - Map payload = new HashMap<>(); - Map entry = new HashMap<>(); - entry.put("b", "bravo"); - List> alpha = Arrays.asList(entry, new HashMap()); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[].b", payload).getValue()) - .isEqualTo(Arrays.asList("bravo", ExtractedField.ABSENT)); - } - - @Test - void extractOccasionallyNullFieldFromItemsInArray() { - Map payload = new HashMap<>(); - Map nonNullField = new HashMap<>(); - nonNullField.put("b", "bravo"); - Map nullField = new HashMap<>(); - nullField.put("b", null); - List> alpha = Arrays.asList(nonNullField, nullField); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[].b", payload).getValue()).isEqualTo(Arrays.asList("bravo", null)); - } - - @Test - void extractNestedArray() { - Map payload = new HashMap<>(); - Map entry1 = createEntry("id:1"); - Map entry2 = createEntry("id:2"); - Map entry3 = createEntry("id:3"); - List>> alpha = Arrays.asList(Arrays.asList(entry1, entry2), Arrays.asList(entry3)); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[][]", payload).getValue()) - .isEqualTo(Arrays.asList(Arrays.asList(entry1, entry2), Arrays.asList(entry3))); - } - - @Test - void extractFromItemsInNestedArray() { - Map payload = new HashMap<>(); - Map entry1 = createEntry("id:1"); - Map entry2 = createEntry("id:2"); - Map entry3 = createEntry("id:3"); - List>> alpha = Arrays.asList(Arrays.asList(entry1, entry2), Arrays.asList(entry3)); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[][].id", payload).getValue()).isEqualTo(Arrays.asList("1", "2", "3")); - } - - @Test - void extractArraysFromItemsInNestedArray() { - Map payload = new HashMap<>(); - Map entry1 = createEntry("ids", Arrays.asList(1, 2)); - Map entry2 = createEntry("ids", Arrays.asList(3)); - Map entry3 = createEntry("ids", Arrays.asList(4)); - List>> alpha = Arrays.asList(Arrays.asList(entry1, entry2), Arrays.asList(entry3)); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a[][].ids", payload).getValue()) - .isEqualTo(Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3), Arrays.asList(4))); - } - - @Test - void nonExistentTopLevelField() { - assertThat(this.fieldProcessor.extract("a", Collections.emptyMap()).getValue()) - .isEqualTo(ExtractedField.ABSENT); - } - - @Test - void nonExistentNestedField() { - HashMap payload = new HashMap<>(); - payload.put("a", new HashMap<>()); - assertThat(this.fieldProcessor.extract("a.b", payload).getValue()).isEqualTo(ExtractedField.ABSENT); - } - - @Test - void nonExistentNestedFieldWhenParentIsNotAMap() { - HashMap payload = new HashMap<>(); - payload.put("a", 5); - assertThat(this.fieldProcessor.extract("a.b", payload).getValue()).isEqualTo(ExtractedField.ABSENT); - } - - @Test - void nonExistentFieldWhenParentIsAnArray() { - HashMap payload = new HashMap<>(); - HashMap alpha = new HashMap<>(); - alpha.put("b", Arrays.asList(new HashMap())); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a.b.c", payload).getValue()).isEqualTo(ExtractedField.ABSENT); - } - - @Test - void nonExistentArrayField() { - HashMap payload = new HashMap<>(); - assertThat(this.fieldProcessor.extract("a[]", payload).getValue()).isEqualTo(ExtractedField.ABSENT); - } - - @Test - void nonExistentArrayFieldAsTypeDoesNotMatch() { - HashMap payload = new HashMap<>(); - payload.put("a", 5); - assertThat(this.fieldProcessor.extract("a[]", payload).getValue()).isEqualTo(ExtractedField.ABSENT); - } - - @Test - void nonExistentFieldBeneathAnArray() { - HashMap payload = new HashMap<>(); - HashMap alpha = new HashMap<>(); - alpha.put("b", Arrays.asList(new HashMap())); - payload.put("a", alpha); - assertThat(this.fieldProcessor.extract("a.b[].id", payload).getValue()) - .isEqualTo(Arrays.asList(ExtractedField.ABSENT)); - } - - @Test - void removeTopLevelMapEntry() { - Map payload = new HashMap<>(); - payload.put("a", "alpha"); - this.fieldProcessor.remove("a", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @Test - void mapWithEntriesIsNotRemovedWhenNotAlsoRemovingDescendants() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo"); - this.fieldProcessor.remove("a", payload); - assertThat(payload.size()).isEqualTo(1); - } - - @Test - void removeSubsectionRemovesMapWithEntries() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo"); - this.fieldProcessor.removeSubsection("a", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @Test - void removeNestedMapEntry() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo"); - this.fieldProcessor.remove("a.b", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @SuppressWarnings("unchecked") - @Test - void removeItemsInArray() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", - Map.class); - this.fieldProcessor.remove("a[].b", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @SuppressWarnings("unchecked") - @Test - void removeItemsInNestedArray() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [[{\"id\":1},{\"id\":2}], [{\"id\":3}]]}", - Map.class); - this.fieldProcessor.remove("a[][].id", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @SuppressWarnings("unchecked") - @Test - void removeDoesNotRemoveArrayWithMapEntries() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", - Map.class); - this.fieldProcessor.remove("a[]", payload); - assertThat(payload.size()).isEqualTo(1); - } - - @SuppressWarnings("unchecked") - @Test - void removeDoesNotRemoveArrayWithListEntries() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [[2],[3]]}", Map.class); - this.fieldProcessor.remove("a[]", payload); - assertThat(payload.size()).isEqualTo(1); - } - - @SuppressWarnings("unchecked") - @Test - void removeRemovesArrayWithOnlyScalarEntries() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [\"bravo\", \"charlie\"]}", Map.class); - this.fieldProcessor.remove("a", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @SuppressWarnings("unchecked") - @Test - void removeSubsectionRemovesArrayWithMapEntries() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [{\"b\":\"bravo\"},{\"b\":\"bravo\"}]}", - Map.class); - this.fieldProcessor.removeSubsection("a[]", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @SuppressWarnings("unchecked") - @Test - void removeSubsectionRemovesArrayWithListEntries() throws IOException { - Map payload = new ObjectMapper().readValue("{\"a\": [[2],[3]]}", Map.class); - this.fieldProcessor.removeSubsection("a[]", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @Test - void extractNestedEntryWithDotInKeys() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a.key", alpha); - alpha.put("b.key", "bravo"); - assertThat(this.fieldProcessor.extract("['a.key']['b.key']", payload).getValue()).isEqualTo("bravo"); - } - - @SuppressWarnings("unchecked") - @Test - void extractNestedEntriesUsingTopLevelWildcard() { - Map payload = new LinkedHashMap<>(); - Map alpha = new LinkedHashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo1"); - Map charlie = new LinkedHashMap<>(); - charlie.put("b", "bravo2"); - payload.put("c", charlie); - assertThat((List) this.fieldProcessor.extract("*.b", payload).getValue()).containsExactly("bravo1", - "bravo2"); - } - - @SuppressWarnings("unchecked") - @Test - void extractNestedEntriesUsingMidLevelWildcard() { - Map payload = new LinkedHashMap<>(); - Map alpha = new LinkedHashMap<>(); - payload.put("a", alpha); - Map bravo = new LinkedHashMap<>(); - bravo.put("b", "bravo"); - alpha.put("one", bravo); - alpha.put("two", bravo); - assertThat((List) this.fieldProcessor.extract("a.*.b", payload).getValue()).containsExactly("bravo", - "bravo"); - } - - @SuppressWarnings("unchecked") - @Test - void extractUsingLeafWildcardMatchingSingleItem() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo1"); - Map charlie = new HashMap<>(); - charlie.put("b", "bravo2"); - payload.put("c", charlie); - assertThat((List) this.fieldProcessor.extract("a.*", payload).getValue()).containsExactly("bravo1"); - } - - @SuppressWarnings("unchecked") - @Test - void extractUsingLeafWildcardMatchingMultipleItems() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo1"); - alpha.put("c", "charlie"); - assertThat((List) this.fieldProcessor.extract("a.*", payload).getValue()).containsExactly("bravo1", - "charlie"); - } - - @Test - void removeUsingLeafWildcard() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo1"); - alpha.put("c", "charlie"); - this.fieldProcessor.remove("a.*", payload); - assertThat(payload.size()).isEqualTo(0); - } - - @Test - void removeUsingTopLevelWildcard() { - Map payload = new HashMap<>(); - Map alpha = new HashMap<>(); - payload.put("a", alpha); - alpha.put("b", "bravo1"); - alpha.put("c", "charlie"); - this.fieldProcessor.remove("*.b", payload); - assertThat(alpha).doesNotContainKey("b"); - } - - @Test - void removeUsingMidLevelWildcard() { - Map payload = new LinkedHashMap<>(); - Map alpha = new LinkedHashMap<>(); - payload.put("a", alpha); - payload.put("c", "charlie"); - Map bravo1 = new LinkedHashMap<>(); - bravo1.put("b", "bravo"); - alpha.put("one", bravo1); - Map bravo2 = new LinkedHashMap<>(); - bravo2.put("b", "bravo"); - alpha.put("two", bravo2); - this.fieldProcessor.remove("a.*.b", payload); - assertThat(payload.size()).isEqualTo(1); - assertThat(payload).containsEntry("c", "charlie"); - } - - @Test - void hasFieldIsTrueForNonNullFieldInMap() { - Map payload = new HashMap<>(); - payload.put("a", "alpha"); - assertThat(this.fieldProcessor.hasField("a", payload)).isTrue(); - } - - @Test - void hasFieldIsTrueForNullFieldInMap() { - Map payload = new HashMap<>(); - payload.put("a", null); - assertThat(this.fieldProcessor.hasField("a", payload)).isTrue(); - } - - @Test - void hasFieldIsFalseForAbsentFieldInMap() { - Map payload = new HashMap<>(); - payload.put("a", null); - assertThat(this.fieldProcessor.hasField("b", payload)).isFalse(); - } - - @Test - void hasFieldIsTrueForNeverNullFieldBeneathArray() { - Map payload = new HashMap<>(); - Map nested = new HashMap<>(); - nested.put("b", "bravo"); - payload.put("a", Arrays.asList(nested, nested, nested)); - assertThat(this.fieldProcessor.hasField("a.[].b", payload)).isTrue(); - } - - @Test - void hasFieldIsTrueForAlwaysNullFieldBeneathArray() { - Map payload = new HashMap<>(); - Map nested = new HashMap<>(); - nested.put("b", null); - payload.put("a", Arrays.asList(nested, nested, nested)); - assertThat(this.fieldProcessor.hasField("a.[].b", payload)).isTrue(); - } - - @Test - void hasFieldIsFalseForAlwaysAbsentFieldBeneathArray() { - Map payload = new HashMap<>(); - Map nested = new HashMap<>(); - nested.put("b", "bravo"); - payload.put("a", Arrays.asList(nested, nested, nested)); - assertThat(this.fieldProcessor.hasField("a.[].c", payload)).isFalse(); - } - - @Test - void hasFieldIsFalseForOccasionallyAbsentFieldBeneathArray() { - Map payload = new HashMap<>(); - Map nested = new HashMap<>(); - nested.put("b", "bravo"); - payload.put("a", Arrays.asList(nested, new HashMap<>(), nested)); - assertThat(this.fieldProcessor.hasField("a.[].b", payload)).isFalse(); - } - - @Test - void hasFieldIsFalseForOccasionallyNullFieldBeneathArray() { - Map payload = new HashMap<>(); - Map fieldPresent = new HashMap<>(); - fieldPresent.put("b", "bravo"); - Map fieldNull = new HashMap<>(); - fieldNull.put("b", null); - payload.put("a", Arrays.asList(fieldPresent, fieldPresent, fieldNull)); - assertThat(this.fieldProcessor.hasField("a.[].b", payload)).isFalse(); - } - - private Map createEntry(String... pairs) { - Map entry = new HashMap<>(); - for (String pair : pairs) { - String[] components = pair.split(":"); - entry.put(components[0], components[1]); - } - return entry; - } - - private Map createEntry(String key, Object value) { - Map entry = new HashMap<>(); - entry.put(key, value); - return entry; - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java deleted file mode 100644 index 4c469005b..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesDiscovererTests.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for {@link JsonFieldTypesDiscoverer}. - * - * @author Andy Wilkinson - */ -class JsonFieldTypesDiscovererTests { - - private final JsonFieldTypesDiscoverer fieldTypeDiscoverer = new JsonFieldTypesDiscoverer(); - - @Test - void arrayField() throws IOException { - assertThat(discoverFieldTypes("[]")).containsExactly(JsonFieldType.ARRAY); - } - - @Test - void topLevelArray() throws IOException { - assertThat(discoverFieldTypes("[]", "[{\"a\":\"alpha\"}]")).containsExactly(JsonFieldType.ARRAY); - } - - @Test - void nestedArray() throws IOException { - assertThat(discoverFieldTypes("a[]", "{\"a\": [{\"b\":\"bravo\"}]}")).containsExactly(JsonFieldType.ARRAY); - } - - @Test - void arrayNestedBeneathAnArray() throws IOException { - assertThat(discoverFieldTypes("a[].b[]", "{\"a\": [{\"b\": [ 1, 2 ]}]}")).containsExactly(JsonFieldType.ARRAY); - } - - @Test - void specificFieldOfObjectInArrayNestedBeneathAnArray() throws IOException { - assertThat(discoverFieldTypes("a[].b[].c", "{\"a\": [{\"b\": [ {\"c\": 5}, {\"c\": 5}]}]}")) - .containsExactly(JsonFieldType.NUMBER); - } - - @Test - void booleanField() throws IOException { - assertThat(discoverFieldTypes("true")).containsExactly(JsonFieldType.BOOLEAN); - } - - @Test - void objectField() throws IOException { - assertThat(discoverFieldTypes("{}")).containsExactly(JsonFieldType.OBJECT); - } - - @Test - void nullField() throws IOException { - assertThat(discoverFieldTypes("null")).containsExactly(JsonFieldType.NULL); - } - - @Test - void numberField() throws IOException { - assertThat(discoverFieldTypes("1.2345")).containsExactly(JsonFieldType.NUMBER); - } - - @Test - void stringField() throws IOException { - assertThat(discoverFieldTypes("\"Foo\"")).containsExactly(JsonFieldType.STRING); - } - - @Test - void nestedField() throws IOException { - assertThat(discoverFieldTypes("a.b.c", "{\"a\":{\"b\":{\"c\":{}}}}")).containsExactly(JsonFieldType.OBJECT); - } - - @Test - void multipleFieldsWithSameType() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":2}]}")) - .containsExactly(JsonFieldType.NUMBER); - } - - @Test - void multipleFieldsWithDifferentTypes() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":true}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.BOOLEAN); - } - - @Test - void multipleFieldsWithDifferentTypesAndSometimesAbsent() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":true}, {}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.BOOLEAN, JsonFieldType.NULL); - } - - @Test - void multipleFieldsWhenSometimesAbsent() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":2}, {}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.NULL); - } - - @Test - void multipleFieldsWhenSometimesNull() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":2}, {\"id\":null}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.NULL); - } - - @Test - void multipleFieldsWithDifferentTypesAndSometimesNull() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":1},{\"id\":true}, {\"id\":null}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.BOOLEAN, JsonFieldType.NULL); - } - - @Test - void multipleFieldsWhenEitherNullOrAbsent() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{},{\"id\":null}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NULL); - } - - @Test - void multipleFieldsThatAreAllNull() throws IOException { - assertThat(discoverFieldTypes("a[].id", "{\"a\":[{\"id\":null},{\"id\":null}]}")) - .containsExactlyInAnyOrder(JsonFieldType.NULL); - } - - @Test - void nonExistentSingleFieldProducesFieldDoesNotExistException() { - assertThatExceptionOfType(FieldDoesNotExistException.class) - .isThrownBy(() -> discoverFieldTypes("a.b", "{\"a\":{}}")) - .withMessage("The payload does not contain a field with the path 'a.b'"); - } - - @Test - void nonExistentMultipleFieldsProducesFieldDoesNotExistException() { - assertThatExceptionOfType(FieldDoesNotExistException.class) - .isThrownBy(() -> discoverFieldTypes("a[].b", "{\"a\":[{\"c\":1},{\"c\":2}]}")) - .withMessage("The payload does not contain a field with the path 'a[].b'"); - } - - @Test - void leafWildcardWithCommonType() throws IOException { - assertThat(discoverFieldTypes("a.*", "{\"a\": {\"b\": 5, \"c\": 6}}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER); - } - - @Test - void leafWildcardWithVaryingType() throws IOException { - assertThat(discoverFieldTypes("a.*", "{\"a\": {\"b\": 5, \"c\": \"six\"}}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.STRING); - } - - @Test - void intermediateWildcardWithCommonType() throws IOException { - assertThat(discoverFieldTypes("a.*.d", "{\"a\": {\"b\": {\"d\": 4}, \"c\": {\"d\": 5}}}}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER); - } - - @Test - void intermediateWildcardWithVaryingType() throws IOException { - assertThat(discoverFieldTypes("a.*.d", "{\"a\": {\"b\": {\"d\": 4}, \"c\": {\"d\": \"four\"}}}}")) - .containsExactlyInAnyOrder(JsonFieldType.NUMBER, JsonFieldType.STRING); - } - - private JsonFieldTypes discoverFieldTypes(String value) throws IOException { - return discoverFieldTypes("field", "{\"field\":" + value + "}"); - } - - private JsonFieldTypes discoverFieldTypes(String path, String json) throws IOException { - return this.fieldTypeDiscoverer.discoverFieldTypes(path, new ObjectMapper().readValue(json, Object.class)); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java deleted file mode 100644 index 1d2a869d4..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/JsonFieldTypesTests.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.EnumSet; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link JsonFieldTypes}. - * - * @author Andy Wilkinson - */ -class JsonFieldTypesTests { - - @Test - void singleTypeCoalescesToThatType() { - assertThat(new JsonFieldTypes(JsonFieldType.NUMBER).coalesce(false)).isEqualTo(JsonFieldType.NUMBER); - } - - @Test - void singleTypeCoalescesToThatTypeWhenOptional() { - assertThat(new JsonFieldTypes(JsonFieldType.NUMBER).coalesce(true)).isEqualTo(JsonFieldType.NUMBER); - } - - @Test - void multipleTypesCoalescesToVaries() { - assertThat(new JsonFieldTypes(EnumSet.of(JsonFieldType.ARRAY, JsonFieldType.NUMBER)).coalesce(false)) - .isEqualTo(JsonFieldType.VARIES); - } - - @Test - void nullAndNonNullTypesCoalescesToVaries() { - assertThat(new JsonFieldTypes(EnumSet.of(JsonFieldType.ARRAY, JsonFieldType.NULL)).coalesce(false)) - .isEqualTo(JsonFieldType.VARIES); - } - - @Test - void nullAndNonNullTypesCoalescesToNonNullTypeWhenOptional() { - assertThat(new JsonFieldTypes(EnumSet.of(JsonFieldType.ARRAY, JsonFieldType.NULL)).coalesce(true)) - .isEqualTo(JsonFieldType.ARRAY); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java deleted file mode 100644 index 11b6df5fa..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/PayloadDocumentationTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.payload.PayloadDocumentation.applyPathPrefix; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link PayloadDocumentation}. - * - * @author Andy Wilkinson - */ -class PayloadDocumentationTests { - - @Test - void applyPathPrefixAppliesPrefixToDescriptorPaths() { - List descriptors = applyPathPrefix("alpha.", - Arrays.asList(fieldWithPath("bravo"), fieldWithPath("charlie"))); - assertThat(descriptors.size()).isEqualTo(2); - assertThat(descriptors.get(0).getPath()).isEqualTo("alpha.bravo"); - } - - @Test - void applyPathPrefixCopiesIgnored() { - List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").ignored())); - assertThat(descriptors.size()).isEqualTo(1); - assertThat(descriptors.get(0).isIgnored()).isTrue(); - } - - @Test - void applyPathPrefixCopiesOptional() { - List descriptors = applyPathPrefix("alpha.", Arrays.asList(fieldWithPath("bravo").optional())); - assertThat(descriptors.size()).isEqualTo(1); - assertThat(descriptors.get(0).isOptional()).isTrue(); - } - - @Test - void applyPathPrefixCopiesDescription() { - List descriptors = applyPathPrefix("alpha.", - Arrays.asList(fieldWithPath("bravo").description("Some field"))); - assertThat(descriptors.size()).isEqualTo(1); - assertThat(descriptors.get(0).getDescription()).isEqualTo("Some field"); - } - - @Test - void applyPathPrefixCopiesType() { - List descriptors = applyPathPrefix("alpha.", - Arrays.asList(fieldWithPath("bravo").type(JsonFieldType.OBJECT))); - assertThat(descriptors.size()).isEqualTo(1); - assertThat(descriptors.get(0).getType()).isEqualTo(JsonFieldType.OBJECT); - } - - @Test - void applyPathPrefixCopiesAttributes() { - List descriptors = applyPathPrefix("alpha.", - Arrays.asList(fieldWithPath("bravo").attributes(key("a").value("alpha"), key("b").value("bravo")))); - assertThat(descriptors.size()).isEqualTo(1); - assertThat(descriptors.get(0).getAttributes().size()).isEqualTo(2); - assertThat(descriptors.get(0).getAttributes().get("a")).isEqualTo("alpha"); - assertThat(descriptors.get(0).getAttributes().get("b")).isEqualTo("bravo"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java deleted file mode 100644 index f9c94dbd9..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodyPartSnippetTests.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link RequestPartBodySnippet}. - * - * @author Andy Wilkinson - */ -class RequestBodyPartSnippetTests { - - @RenderedSnippetTest - void requestPartWithBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestPartBody("one") - .document(operationBuilder.request("http://localhost").part("one", "some content".getBytes()).build()); - assertThat(snippets.requestPartBody("one")) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("some content")); - } - - @RenderedSnippetTest - void requestPartWithNoBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestPartBody("one").document(operationBuilder.request("http://localhost").part("one", new byte[0]).build()); - assertThat(snippets.requestPartBody("one")) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("")); - } - - @RenderedSnippetTest - void requestPartWithJsonMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestPartBody("one").document(operationBuilder.request("http://localhost") - .part("one", "".getBytes()) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .build()); - assertThat(snippets.requestPartBody("one")) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); - } - - @RenderedSnippetTest - void requestPartWithJsonSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestPartBody("one").document(operationBuilder.request("http://localhost") - .part("one", "".getBytes()) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_VALUE) - .build()); - assertThat(snippets.requestPartBody("one")) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); - } - - @RenderedSnippetTest - void requestPartWithXmlMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestPartBody("one").document(operationBuilder.request("http://localhost") - .part("one", "".getBytes()) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(snippets.requestPartBody("one")) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); - } - - @RenderedSnippetTest - void requestPartWithXmlSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestPartBody("one").document(operationBuilder.request("http://localhost") - .part("one", "".getBytes()) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML_VALUE) - .build()); - assertThat(snippets.requestPartBody("one")) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); - } - - @RenderedSnippetTest - void subsectionOfRequestPartBody(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestPartBody("one", beneathPath("a.b")).document(operationBuilder.request("http://localhost") - .part("one", "{\"a\":{\"b\":{\"c\":5}}}".getBytes()) - .build()); - assertThat(snippets.requestPartBody("one", "beneath-a.b")) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("{\"c\":5}")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-part-body", template = "request-part-body-with-language") - void customSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestPartBody("one", attributes(key("language").value("json"))) - .document(operationBuilder.request("http://localhost").part("one", "{\"a\":\"alpha\"}".getBytes()).build()); - assertThat(snippets.requestPartBody("one")).isCodeBlock( - (codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("{\"a\":\"alpha\"}")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java deleted file mode 100644 index c332fdcc7..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestBodySnippetTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestBody; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link RequestBodySnippet}. - * - * @author Andy Wilkinson - */ -class RequestBodySnippetTests { - - @RenderedSnippetTest - void requestWithBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestBody().document(operationBuilder.request("http://localhost").content("some content").build()); - assertThat(snippets.requestBody()) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("some content")); - } - - @RenderedSnippetTest - void requestWithNoBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestBody().document(operationBuilder.request("http://localhost").build()); - assertThat(snippets.requestBody()).isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("")); - } - - @RenderedSnippetTest - void requestWithJsonMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestBody().document(operationBuilder.request("http://localhost") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .build()); - assertThat(snippets.requestBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); - } - - @RenderedSnippetTest - void requestWithJsonSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestBody().document(operationBuilder.request("http://localhost") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_VALUE) - .build()); - assertThat(snippets.requestBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); - } - - @RenderedSnippetTest - void requestWithXmlMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestBody().document(operationBuilder.request("http://localhost") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(snippets.requestBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); - } - - @RenderedSnippetTest - void requestWithXmlSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestBody().document(operationBuilder.request("http://localhost") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML_VALUE) - .build()); - assertThat(snippets.requestBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); - } - - @RenderedSnippetTest - void subsectionOfRequestBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestBody(beneathPath("a.b")) - .document(operationBuilder.request("http://localhost").content("{\"a\":{\"b\":{\"c\":5}}}").build()); - assertThat(snippets.requestBody("beneath-a.b")) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("{\"c\":5}")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-body", template = "request-body-with-language") - void customSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestBody(attributes(key("language").value("json"))) - .document(operationBuilder.request("http://localhost").content("{\"a\":\"alpha\"}").build()); - assertThat(snippets.requestBody()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("{\"a\":\"alpha\"}")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java deleted file mode 100644 index 7c6394d96..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestFieldsSnippetTests.java +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link RequestFieldsSnippet}. - * - * @author Andy Wilkinson - * @author Sungjun Lee - */ -class RequestFieldsSnippetTests { - - @RenderedSnippetTest - void mapRequestWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"), - fieldWithPath("a.c").description("two"), fieldWithPath("a").description("three"))) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); - } - - @RenderedSnippetTest - void mapRequestWithNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(operationBuilder.request("http://localhost").content("{\"a\": {\"b\": null}}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`Null`", "one")); - } - - @RenderedSnippetTest - void entireSubsectionsCanBeDocumented(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(subsectionWithPath("a").description("one"))) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Object`", "one")); - } - - @RenderedSnippetTest - void subsectionOfMapRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestFields(beneathPath("a"), fieldWithPath("b").description("one"), fieldWithPath("c").description("two")) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(snippets.requestFields("beneath-a")) - .isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`b`", "`Number`", "one") - .row("`c`", "`String`", "two")); - } - - @RenderedSnippetTest - void subsectionOfMapRequestWithCommonPrefix(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - requestFields(beneathPath("a")).andWithPrefix("b.", fieldWithPath("c").description("two")) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": {\"c\": \"charlie\"}}}") - .build()); - assertThat(snippets.requestFields("beneath-a")) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b.c`", "`String`", "two")); - } - - @RenderedSnippetTest - void arrayRequestWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("[]").description("one"), fieldWithPath("[]a.b").description("two"), - fieldWithPath("[]a.c").description("three"), fieldWithPath("[]a").description("four"))) - .document(operationBuilder.request("http://localhost") - .content("[{\"a\": {\"b\": 5, \"c\":\"charlie\"}}," + "{\"a\": {\"b\": 4, \"c\":\"chalk\"}}]") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`[]`", "`Array`", "one") - .row("`[]a.b`", "`Number`", "two") - .row("`[]a.c`", "`String`", "three") - .row("`[]a`", "`Object`", "four")); - } - - @RenderedSnippetTest - void arrayRequestWithAlwaysNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"))) - .document(operationBuilder.request("http://localhost") - .content("[{\"a\": {\"b\": null}}," + "{\"a\": {\"b\": null}}]") - .build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`[]a.b`", "`Null`", "one")); - } - - @RenderedSnippetTest - void subsectionOfArrayRequest(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - requestFields(beneathPath("[].a"), fieldWithPath("b").description("one"), fieldWithPath("c").description("two")) - .document(operationBuilder.request("http://localhost") - .content("[{\"a\": {\"b\": 5, \"c\": \"charlie\"}}]") - .build()); - assertThat(snippets.requestFields("beneath-[].a")) - .isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`b`", "`Number`", "one") - .row("`c`", "`String`", "two")); - } - - @RenderedSnippetTest - void ignoredRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").ignored(), fieldWithPath("b").description("Field b"))) - .document(operationBuilder.request("http://localhost").content("{\"a\": 5, \"b\": 4}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); - } - - @RenderedSnippetTest - void entireSubsectionCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet( - Arrays.asList(subsectionWithPath("a").ignored(), fieldWithPath("c").description("Field c"))) - .document(operationBuilder.request("http://localhost").content("{\"a\": {\"b\": 5}, \"c\": 4}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`c`", "`Number`", "Field c")); - } - - @RenderedSnippetTest - void allUndocumentedRequestFieldsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) - .document(operationBuilder.request("http://localhost").content("{\"a\": 5, \"b\": 4}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); - } - - @RenderedSnippetTest - void allUndocumentedFieldsContinueToBeIgnoredAfterAddingDescriptors(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) - .andWithPrefix("c.", fieldWithPath("d").description("Field d")) - .document( - operationBuilder.request("http://localhost").content("{\"a\":5,\"b\":4,\"c\":{\"d\": 3}}").build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`b`", "`Number`", "Field b") - .row("`c.d`", "`Number`", "Field d")); - } - - @RenderedSnippetTest - void missingOptionalRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(operationBuilder.request("http://localhost").content("{}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); - } - - @RenderedSnippetTest - void missingIgnoredOptionalRequestFieldDoesNotRequireAType(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").ignored().optional())) - .document(operationBuilder.request("http://localhost").content("{}").build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description")); - } - - @RenderedSnippetTest - void presentOptionalRequestField(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(operationBuilder.request("http://localhost").content("{\"a\": { \"b\": \"bravo\"}}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-fields", template = "request-fields-with-title") - void requestFieldsWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), - attributes(key("title").value("Custom title"))) - .document(operationBuilder.request("http://localhost").content("{\"a\": \"foo\"}").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withTitleAndHeader("Custom title", "Path", "Type", "Description") - .row("a", "String", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-fields", template = "request-fields-with-extra-column") - void requestFieldsWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a.b").description("one").attributes(key("foo").value("alpha")), - fieldWithPath("a.c").description("two").attributes(key("foo").value("bravo")), - fieldWithPath("a").description("three").attributes(key("foo").value("charlie")))) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description", "Foo") - .row("a.b", "Number", "one", "alpha") - .row("a.c", "String", "two", "bravo") - .row("a", "Object", "three", "charlie")); - } - - @RenderedSnippetTest - void fieldWithExplicitExactlyMatchingType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.NUMBER))) - .document(operationBuilder.request("http://localhost").content("{\"a\": 5 }").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Number`", "one")); - } - - @RenderedSnippetTest - void fieldWithExplicitVariesType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.VARIES))) - .document(operationBuilder.request("http://localhost").content("{\"a\": 5 }").build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Varies`", "one")); - } - - @RenderedSnippetTest - void applicationXmlRequestFields(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - xmlRequestFields(MediaType.APPLICATION_XML, operationBuilder, snippets); - } - - @RenderedSnippetTest - void textXmlRequestFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - xmlRequestFields(MediaType.TEXT_XML, operationBuilder, snippets); - } - - @RenderedSnippetTest - void customXmlRequestFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - xmlRequestFields(MediaType.parseMediaType("application/vnd.com.example+xml"), operationBuilder, snippets); - } - - private void xmlRequestFields(MediaType contentType, OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one").type("b"), - fieldWithPath("a/c").description("two").type("c"), fieldWithPath("a").description("three").type("a"))) - .document(operationBuilder.request("http://localhost") - .content("5charlie") - .header(HttpHeaders.CONTENT_TYPE, contentType.toString()) - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a/b`", "`b`", "one") - .row("`a/c`", "`c`", "two") - .row("`a`", "`a`", "three")); - } - - @RenderedSnippetTest - void entireSubsectionOfXmlPayloadCanBeDocumented(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(subsectionWithPath("a").description("one").type("a"))) - .document(operationBuilder.request("http://localhost") - .content("5charlie") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`a`", "one")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - PayloadDocumentation - .requestFields(fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two")) - .and(fieldWithPath("a").description("three")) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); - } - - @RenderedSnippetTest - void prefixedAdditionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - PayloadDocumentation.requestFields(fieldWithPath("a").description("one")) - .andWithPrefix("a.", fieldWithPath("b").description("two"), fieldWithPath("c").description("three")) - .document(operationBuilder.request("http://localhost") - .content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a`", "`Object`", "one") - .row("`a.b`", "`Number`", "two") - .row("`a.c`", "`String`", "three")); - } - - @RenderedSnippetTest - void requestWithFieldsWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("Foo|Bar").type("one|two").description("three|four"))) - .document(operationBuilder.request("http://localhost").content("{\"Foo|Bar\": 5}").build()); - assertThat(snippets.requestFields()).isTable( - (table) -> table.withHeader("Path", "Type", "Description").row("`Foo|Bar`", "`one|two`", "three|four")); - } - - @RenderedSnippetTest - void mapRequestWithVaryingKeysMatchedUsingWildcard(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet(Arrays.asList(fieldWithPath("things.*.size").description("one"), - fieldWithPath("things.*.type").description("two"))) - .document(operationBuilder.request("http://localhost") - .content("{\"things\": {\"12abf\": {\"type\":" + "\"Whale\", \"size\": \"HUGE\"}," - + "\"gzM33\" : {\"type\": \"Screw\"," + "\"size\": \"SMALL\"}}}") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`things.*.size`", "`String`", "one") - .row("`things.*.type`", "`String`", "two")); - } - - @RenderedSnippetTest - void requestWithArrayContainingFieldThatIsSometimesNull(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("assets[].name").description("one").type(JsonFieldType.STRING).optional())) - .document(operationBuilder.request("http://localhost") - .content("{\"assets\": [" + "{\"name\": \"sample1\"}, " + "{\"name\": null}, " - + "{\"name\": \"sample2\"}]}") - .build()); - assertThat(snippets.requestFields()).isTable( - (table) -> table.withHeader("Path", "Type", "Description").row("`assets[].name`", "`String`", "one")); - } - - @RenderedSnippetTest - void optionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER).optional(), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(operationBuilder.request("http://localhost") - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") - .build()); - assertThat(snippets.requestFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a[].b`", "`Number`", "one") - .row("`a[].c`", "`Number`", "two")); - } - - @RenderedSnippetTest - void typeDeterminationDoesNotSetTypeOnDescriptor(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - FieldDescriptor descriptor = fieldWithPath("a.b").description("one"); - new RequestFieldsSnippet(Arrays.asList(descriptor)) - .document(operationBuilder.request("http://localhost").content("{\"a\": {\"b\": 5}}").build()); - assertThat(descriptor.getType()).isNull(); - assertThat(snippets.requestFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`Number`", "one")); - } - - @SnippetTest - void undocumentedRequestField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) - .document(operationBuilder.request("http://localhost").content("{\"a\": 5}").build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @SnippetTest - void missingRequestField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(operationBuilder.request("http://localhost").content("{}").build())) - .withMessage("Fields with the following paths were not found in the payload: [a.b]"); - } - - @SnippetTest - void missingOptionalRequestFieldWithNoTypeProvided(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypeRequiredException.class).isThrownBy( - () -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").optional())) - .document(operationBuilder.request("http://localhost").content("{ }").build())); - } - - @SnippetTest - void undocumentedRequestFieldAndMissingRequestField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(operationBuilder.request("http://localhost").content("{ \"a\": { \"c\": 5 }}").build())) - .withMessageStartingWith("The following parts of the payload were not documented:") - .withMessageEndingWith("Fields with the following paths were not found in the payload: [a.b]"); - } - - @SnippetTest - void attemptToDocumentFieldsWithNoRequestBody(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(operationBuilder.request("http://localhost").build())) - .withMessage("Cannot document request fields as the request body is empty"); - } - - @SnippetTest - void fieldWithExplicitTypeThatDoesNotMatchThePayload(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.OBJECT))) - .document(operationBuilder.request("http://localhost").content("{ \"a\": 5 }").build())) - .withMessage("The documented type of the field 'a' is Object but the actual type is Number"); - } - - @SnippetTest - void fieldWithExplicitSpecificTypeThatActuallyVaries(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class).isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("[].a").description("one").type(JsonFieldType.OBJECT))) - .document(operationBuilder.request("http://localhost").content("[{ \"a\": 5 },{ \"a\": \"b\" }]").build())) - .withMessage("The documented type of the field '[].a' is Object but the actual type is Varies"); - } - - @SnippetTest - void undocumentedXmlRequestField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) - .document(operationBuilder.request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @SnippetTest - void xmlDescendentsAreNotDocumentedByFieldDescriptor(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").type("a").description("one"))) - .document(operationBuilder.request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @SnippetTest - void xmlRequestFieldWithNoType(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypeRequiredException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(operationBuilder.request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())); - } - - @SnippetTest - void missingXmlRequestField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a/b").description("one"), fieldWithPath("a").description("one"))) - .document(operationBuilder.request("http://localhost") - .content("") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage("Fields with the following paths were not found in the payload: [a/b]"); - } - - @SnippetTest - void undocumentedXmlRequestFieldAndMissingXmlRequestField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) - .document(operationBuilder.request("http://localhost") - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:") - .withMessageEndingWith("Fields with the following paths were not found in the payload: [a/b]"); - } - - @SnippetTest - void unsupportedContent(OperationBuilder operationBuilder) { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new RequestFieldsSnippet(Collections.emptyList()) - .document(operationBuilder.request("http://localhost") - .content("Some plain text") - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .build())) - .withMessage("Cannot handle text/plain content as it could not be parsed as JSON or XML"); - } - - @SnippetTest - void nonOptionalFieldBeneathArrayThatIsSometimesNull(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(operationBuilder.request("http://localhost") - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"b\": null, \"c\": 2}," + " {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - - @SnippetTest - void nonOptionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(operationBuilder.request("http://localhost") - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java deleted file mode 100644 index f4924d752..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/RequestPartFieldsSnippetTests.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; - -/** - * Tests for {@link RequestPartFieldsSnippet}. - * - * @author Mathieu Pousse - * @author Andy Wilkinson - */ -public class RequestPartFieldsSnippetTests { - - @RenderedSnippetTest - void mapRequestPartFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestPartFieldsSnippet("one", - Arrays.asList(fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two"), - fieldWithPath("a").description("three"))) - .document(operationBuilder.request("http://localhost") - .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) - .build()); - assertThat(snippets.requestPartFields("one")).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); - } - - @RenderedSnippetTest - void mapRequestPartSubsectionFields(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestPartFieldsSnippet("one", beneathPath("a"), - Arrays.asList(fieldWithPath("b").description("one"), fieldWithPath("c").description("two"))) - .document(operationBuilder.request("http://localhost") - .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) - .build()); - assertThat(snippets.requestPartFields("one", "beneath-a")) - .isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`b`", "`Number`", "one") - .row("`c`", "`String`", "two")); - } - - @RenderedSnippetTest - void multipleRequestParts(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - Operation operation = operationBuilder.request("http://localhost") - .part("one", "{}".getBytes()) - .and() - .part("two", "{}".getBytes()) - .build(); - new RequestPartFieldsSnippet("one", Collections.emptyList()).document(operation); - new RequestPartFieldsSnippet("two", Collections.emptyList()).document(operation); - assertThat(snippets.requestPartFields("one")).isNotNull(); - assertThat(snippets.requestPartFields("two")).isNotNull(); - } - - @RenderedSnippetTest - void allUndocumentedRequestPartFieldsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestPartFieldsSnippet("one", Arrays.asList(fieldWithPath("b").description("Field b")), true).document( - operationBuilder.request("http://localhost").part("one", "{\"a\": 5, \"b\": 4}".getBytes()).build()); - assertThat(snippets.requestPartFields("one")) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - PayloadDocumentation - .requestPartFields("one", fieldWithPath("a.b").description("one"), fieldWithPath("a.c").description("two")) - .and(fieldWithPath("a").description("three")) - .document(operationBuilder.request("http://localhost") - .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) - .build()); - assertThat(snippets.requestPartFields("one")).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a.b`", "`Number`", "one") - .row("`a.c`", "`String`", "two") - .row("`a`", "`Object`", "three")); - } - - @RenderedSnippetTest - void prefixedAdditionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - PayloadDocumentation.requestPartFields("one", fieldWithPath("a").description("one")) - .andWithPrefix("a.", fieldWithPath("b").description("two"), fieldWithPath("c").description("three")) - .document(operationBuilder.request("http://localhost") - .part("one", "{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes()) - .build()); - assertThat(snippets.requestPartFields("one")).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a`", "`Object`", "one") - .row("`a.b`", "`Number`", "two") - .row("`a.c`", "`String`", "three")); - } - - @SnippetTest - void undocumentedRequestPartField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartFieldsSnippet("part", Collections.emptyList()) - .document(operationBuilder.request("http://localhost").part("part", "{\"a\": 5}".getBytes()).build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @SnippetTest - void missingRequestPartField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartFieldsSnippet("part", Arrays.asList(fieldWithPath("b").description("one"))) - .document(operationBuilder.request("http://localhost").part("part", "{\"a\": 5}".getBytes()).build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @SnippetTest - void missingRequestPart(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class).isThrownBy( - () -> new RequestPartFieldsSnippet("another", Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(operationBuilder.request("http://localhost") - .part("part", "{\"a\": {\"b\": 5}}".getBytes()) - .build())) - .withMessage("A request part named 'another' was not found in the request"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java deleted file mode 100644 index 74c659954..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseBodySnippetTests.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link ResponseBodySnippet}. - * - * @author Andy Wilkinson - */ -class ResponseBodySnippetTests { - - @RenderedSnippetTest - void responseWithBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseBodySnippet().document(operationBuilder.response().content("some content").build()); - assertThat(snippets.responseBody()) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("some content")); - } - - @RenderedSnippetTest - void responseWithNoBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseBodySnippet().document(operationBuilder.response().build()); - assertThat(snippets.responseBody()).isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("")); - } - - @RenderedSnippetTest - void responseWithJsonMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseBodySnippet().document( - operationBuilder.response().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build()); - assertThat(snippets.responseBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); - } - - @RenderedSnippetTest - void responseWithJsonSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseBodySnippet().document(operationBuilder.response() - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PROBLEM_JSON_VALUE) - .build()); - assertThat(snippets.responseBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("")); - } - - @RenderedSnippetTest - void responseWithXmlMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseBodySnippet().document( - operationBuilder.response().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE).build()); - assertThat(snippets.responseBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); - } - - @RenderedSnippetTest - void responseWithXmlSubtypeMediaType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseBodySnippet().document(operationBuilder.response() - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_ATOM_XML_VALUE) - .build()); - assertThat(snippets.responseBody()) - .isCodeBlock((codeBlock) -> codeBlock.withLanguageAndOptions("xml", "nowrap").content("")); - } - - @RenderedSnippetTest - void subsectionOfResponseBody(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - responseBody(beneathPath("a.b")) - .document(operationBuilder.response().content("{\"a\":{\"b\":{\"c\":5}}}").build()); - assertThat(snippets.responseBody("beneath-a.b")) - .isCodeBlock((codeBlock) -> codeBlock.withOptions("nowrap").content("{\"c\":5}")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-body", template = "response-body-with-language") - void customSnippetAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseBodySnippet(attributes(key("language").value("json"))) - .document(operationBuilder.response().content("{\"a\":\"alpha\"}").build()); - assertThat(snippets.responseBody()).isCodeBlock( - (codeBlock) -> codeBlock.withLanguageAndOptions("json", "nowrap").content("{\"a\":\"alpha\"}")); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java deleted file mode 100644 index e238851b9..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/ResponseFieldsSnippetTests.java +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link ResponseFieldsSnippet}. - * - * @author Andy Wilkinson - * @author Sungjun Lee - */ -public class ResponseFieldsSnippetTests { - - @RenderedSnippetTest - void mapResponseWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("id").description("one"), - fieldWithPath("date").description("two"), fieldWithPath("assets").description("three"), - fieldWithPath("assets[]").description("four"), fieldWithPath("assets[].id").description("five"), - fieldWithPath("assets[].name").description("six"))) - .document(operationBuilder.response() - .content("{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":" + " [{\"id\":356,\"name\": \"sample\"}]}") - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`id`", "`Number`", "one") - .row("`date`", "`String`", "two") - .row("`assets`", "`Array`", "three") - .row("`assets[]`", "`Array`", "four") - .row("`assets[].id`", "`Number`", "five") - .row("`assets[].name`", "`String`", "six")); - } - - @RenderedSnippetTest - void mapResponseWithNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"))) - .document(operationBuilder.response().content("{\"a\": {\"b\": null}}").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`Null`", "one")); - } - - @RenderedSnippetTest - void subsectionOfMapResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - responseFields(beneathPath("a"), fieldWithPath("b").description("one"), fieldWithPath("c").description("two")) - .document(operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); - assertThat(snippets.responseFields("beneath-a")) - .isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`b`", "`Number`", "one") - .row("`c`", "`String`", "two")); - } - - @RenderedSnippetTest - void subsectionOfMapResponseBeneathAnArray(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - responseFields(beneathPath("a.b.[]"), fieldWithPath("c").description("one"), - fieldWithPath("d.[].e").description("two")) - .document(operationBuilder.response() - .content("{\"a\": {\"b\": [{\"c\": 1, \"d\": [{\"e\": 5}]}, {\"c\": 3, \"d\": [{\"e\": 4}]}]}}") - .build()); - assertThat(snippets.responseFields("beneath-a.b.[]")) - .isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`c`", "`Number`", "one") - .row("`d.[].e`", "`Number`", "two")); - } - - @RenderedSnippetTest - void subsectionOfMapResponseWithCommonsPrefix(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - responseFields(beneathPath("a")).andWithPrefix("b.", fieldWithPath("c").description("two")) - .document(operationBuilder.response().content("{\"a\": {\"b\": {\"c\": \"charlie\"}}}").build()); - assertThat(snippets.responseFields("beneath-a")) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b.c`", "`String`", "two")); - } - - @RenderedSnippetTest - void arrayResponseWithFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"), - fieldWithPath("[]a.c").description("two"), fieldWithPath("[]a").description("three"))) - .document(operationBuilder.response() - .content("[{\"a\": {\"b\": 5, \"c\":\"charlie\"}}," + "{\"a\": {\"b\": 4, \"c\":\"chalk\"}}]") - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`[]a.b`", "`Number`", "one") - .row("`[]a.c`", "`String`", "two") - .row("`[]a`", "`Object`", "three")); - } - - @RenderedSnippetTest - void arrayResponseWithAlwaysNullField(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"))).document( - operationBuilder.response().content("[{\"a\": {\"b\": null}}," + "{\"a\": {\"b\": null}}]").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`[]a.b`", "`Null`", "one")); - } - - @RenderedSnippetTest - void arrayResponse(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]").description("one"))) - .document(operationBuilder.response().content("[\"a\", \"b\", \"c\"]").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`[]`", "`Array`", "one")); - } - - @RenderedSnippetTest - void ignoredResponseField(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a").ignored(), fieldWithPath("b").description("Field b"))) - .document(operationBuilder.response().content("{\"a\": 5, \"b\": 4}").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); - } - - @RenderedSnippetTest - void allUndocumentedFieldsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) - .document(operationBuilder.response().content("{\"a\": 5, \"b\": 4}").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`b`", "`Number`", "Field b")); - } - - @RenderedSnippetTest - void allUndocumentedFieldsContinueToBeIgnoredAfterAddingDescriptors(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("b").description("Field b")), true) - .andWithPrefix("c.", fieldWithPath("d").description("Field d")) - .document(operationBuilder.response().content("{\"a\":5,\"b\":4,\"c\":{\"d\": 3}}").build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`b`", "`Number`", "Field b") - .row("`c.d`", "`Number`", "Field d")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-fields", template = "response-fields-with-title") - void responseFieldsWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")), - attributes(key("title").value("Custom title"))) - .document(operationBuilder.response().content("{\"a\": \"foo\"}").build()); - assertThat(snippets.responseFields()).contains("Custom title"); - } - - @RenderedSnippetTest - void missingOptionalResponseField(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(operationBuilder.response().content("{}").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); - } - - @RenderedSnippetTest - void missingIgnoredOptionalResponseFieldDoesNotRequireAType(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one").ignored().optional())) - .document(operationBuilder.response().content("{}").build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description")); - } - - @RenderedSnippetTest - void presentOptionalResponseField(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a.b").description("one").type(JsonFieldType.STRING).optional())) - .document(operationBuilder.response().content("{\"a\": { \"b\": \"bravo\"}}").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a.b`", "`String`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "response-fields", template = "response-fields-with-extra-column") - void responseFieldsWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a.b").description("one").attributes(key("foo").value("alpha")), - fieldWithPath("a.c").description("two").attributes(key("foo").value("bravo")), - fieldWithPath("a").description("three").attributes(key("foo").value("charlie")))) - .document(operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description", "Foo") - .row("a.b", "Number", "one", "alpha") - .row("a.c", "String", "two", "bravo") - .row("a", "Object", "three", "charlie")); - } - - @RenderedSnippetTest - void fieldWithExplicitExactlyMatchingType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.NUMBER))) - .document(operationBuilder.response().content("{\"a\": 5 }").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Number`", "one")); - } - - @RenderedSnippetTest - void fieldWithExplicitVariesType(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.VARIES))) - .document(operationBuilder.response().content("{\"a\": 5 }").build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`Varies`", "one")); - } - - @RenderedSnippetTest - void applicationXmlResponseFields(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - xmlResponseFields(MediaType.APPLICATION_XML, operationBuilder, snippets); - } - - @RenderedSnippetTest - void textXmlResponseFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - xmlResponseFields(MediaType.TEXT_XML, operationBuilder, snippets); - } - - @RenderedSnippetTest - void customXmlResponseFields(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - xmlResponseFields(MediaType.parseMediaType("application/vnd.com.example+xml"), operationBuilder, snippets); - } - - private void xmlResponseFields(MediaType contentType, OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one").type("b"), - fieldWithPath("a/c").description("two").type("c"), fieldWithPath("a").description("three").type("a"))) - .document(operationBuilder.response() - .content("5charlie") - .header(HttpHeaders.CONTENT_TYPE, contentType.toString()) - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a/b`", "`b`", "one") - .row("`a/c`", "`c`", "two") - .row("`a`", "`a`", "three")); - } - - @RenderedSnippetTest - void xmlAttribute(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), - fieldWithPath("a/@id").description("two").type("c"))) - .document(operationBuilder.response() - .content("foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a`", "`b`", "one") - .row("`a/@id`", "`c`", "two")); - } - - @RenderedSnippetTest - void missingOptionalXmlAttribute(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), - fieldWithPath("a/@id").description("two").type("c").optional())) - .document(operationBuilder.response() - .content("foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a`", "`b`", "one") - .row("`a/@id`", "`c`", "two")); - } - - @RenderedSnippetTest - void undocumentedAttributeDoesNotCauseFailure(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("a"))) - .document(operationBuilder.response() - .content("bar") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build()); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`a`", "`a`", "one")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - PayloadDocumentation - .responseFields(fieldWithPath("id").description("one"), fieldWithPath("date").description("two"), - fieldWithPath("assets").description("three")) - .and(fieldWithPath("assets[]").description("four"), fieldWithPath("assets[].id").description("five"), - fieldWithPath("assets[].name").description("six")) - .document(operationBuilder.response() - .content("{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":" + " [{\"id\":356,\"name\": \"sample\"}]}") - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`id`", "`Number`", "one") - .row("`date`", "`String`", "two") - .row("`assets`", "`Array`", "three") - .row("`assets[]`", "`Array`", "four") - .row("`assets[].id`", "`Number`", "five") - .row("`assets[].name`", "`String`", "six")); - } - - @RenderedSnippetTest - void prefixedAdditionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - PayloadDocumentation.responseFields(fieldWithPath("a").description("one")) - .andWithPrefix("a.", fieldWithPath("b").description("two"), fieldWithPath("c").description("three")) - .document(operationBuilder.response().content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a`", "`Object`", "one") - .row("`a.b`", "`Number`", "two") - .row("`a.c`", "`String`", "three")); - } - - @RenderedSnippetTest - void responseWithFieldsWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("Foo|Bar").type("one|two").description("three|four"))) - .document(operationBuilder.response().content("{\"Foo|Bar\": 5}").build()); - assertThat(snippets.responseFields()).isTable( - (table) -> table.withHeader("Path", "Type", "Description").row("`Foo|Bar`", "`one|two`", "three|four")); - } - - @RenderedSnippetTest - void mapResponseWithVaryingKeysMatchedUsingWildcard(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("things.*.size").description("one"), - fieldWithPath("things.*.type").description("two"))) - .document(operationBuilder.response() - .content("{\"things\": {\"12abf\": {\"type\":" + "\"Whale\", \"size\": \"HUGE\"}," - + "\"gzM33\" : {\"type\": \"Screw\"," + "\"size\": \"SMALL\"}}}") - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`things.*.size`", "`String`", "one") - .row("`things.*.type`", "`String`", "two")); - } - - @RenderedSnippetTest - void responseWithArrayContainingFieldThatIsSometimesNull(OperationBuilder operationBuilder, - AssertableSnippets snippets) throws IOException { - new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("assets[].name").description("one").type(JsonFieldType.STRING).optional())) - .document(operationBuilder.response() - .content("{\"assets\": [" + "{\"name\": \"sample1\"}, " + "{\"name\": null}, " - + "{\"name\": \"sample2\"}]}") - .build()); - assertThat(snippets.responseFields()).isTable( - (table) -> table.withHeader("Path", "Type", "Description").row("`assets[].name`", "`String`", "one")); - } - - @RenderedSnippetTest - void optionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER).optional(), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(operationBuilder.response() - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") - .build()); - assertThat(snippets.responseFields()).isTable((table) -> table.withHeader("Path", "Type", "Description") - .row("`a[].b`", "`Number`", "one") - .row("`a[].c`", "`Number`", "two")); - } - - @RenderedSnippetTest - void typeDeterminationDoesNotSetTypeOnDescriptor(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - FieldDescriptor descriptor = fieldWithPath("id").description("one"); - new ResponseFieldsSnippet(Arrays.asList(descriptor)) - .document(operationBuilder.response().content("{\"id\": 67}").build()); - assertThat(descriptor.getType()).isNull(); - assertThat(snippets.responseFields()) - .isTable((table) -> table.withHeader("Path", "Type", "Description").row("`id`", "`Number`", "one")); - } - - @SnippetTest - void attemptToDocumentFieldsWithNoResponseBody(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(operationBuilder.build())) - .withMessage("Cannot document response fields as the response body is empty"); - } - - @SnippetTest - void fieldWithExplicitTypeThatDoesNotMatchThePayload(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a").description("one").type(JsonFieldType.OBJECT))) - .document(operationBuilder.response().content("{ \"a\": 5 }}").build())) - .withMessage("The documented type of the field 'a' is Object but the actual type is Number"); - } - - @SnippetTest - void fieldWithExplicitSpecificTypeThatActuallyVaries(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypesDoNotMatchException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("[].a").description("one").type(JsonFieldType.OBJECT))) - .document(operationBuilder.response().content("[{ \"a\": 5 },{ \"a\": \"b\" }]").build())) - .withMessage("The documented type of the field '[].a' is Object but the actual type is Varies"); - } - - @SnippetTest - void undocumentedXmlResponseField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Collections.emptyList()) - .document(operationBuilder.response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:"); - } - - @SnippetTest - void missingXmlAttribute(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one").type("b"), - fieldWithPath("a/@id").description("two").type("c"))) - .document(operationBuilder.response() - .content("foo") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage("Fields with the following paths were not found in the payload: [a/@id]"); - } - - @SnippetTest - void documentedXmlAttributesAreRemoved(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy( - () -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one").type("a"))) - .document(operationBuilder.response() - .content("bar") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage(String.format("The following parts of the payload were not documented:%nbar%n")); - } - - @SnippetTest - void xmlResponseFieldWithNoType(OperationBuilder operationBuilder) { - assertThatExceptionOfType(FieldTypeRequiredException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one"))) - .document(operationBuilder.response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())); - } - - @SnippetTest - void missingXmlResponseField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a/b").description("one"), fieldWithPath("a").description("one"))) - .document(operationBuilder.response() - .content("") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessage("Fields with the following paths were not found in the payload: [a/b]"); - } - - @SnippetTest - void undocumentedXmlResponseFieldAndMissingXmlResponseField(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"))) - .document(operationBuilder.response() - .content("5") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE) - .build())) - .withMessageStartingWith("The following parts of the payload were not documented:") - .withMessageEndingWith("Fields with the following paths were not found in the payload: [a/b]"); - } - - @SnippetTest - void unsupportedContent(OperationBuilder operationBuilder) { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> new ResponseFieldsSnippet(Collections.emptyList()) - .document(operationBuilder.response() - .content("Some plain text") - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .build())) - .withMessage("Cannot handle text/plain content as it could not be parsed as JSON or XML"); - } - - @SnippetTest - void nonOptionalFieldBeneathArrayThatIsSometimesNull(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(operationBuilder.response() - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"b\": null, \"c\": 2}," + " {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - - @SnippetTest - void nonOptionalFieldBeneathArrayThatIsSometimesAbsent(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new ResponseFieldsSnippet( - Arrays.asList(fieldWithPath("a[].b").description("one").type(JsonFieldType.NUMBER), - fieldWithPath("a[].c").description("two").type(JsonFieldType.NUMBER))) - .document(operationBuilder.response() - .content("{\"a\":[{\"b\": 1,\"c\": 2}, " + "{\"c\": 2}, {\"b\": 1,\"c\": 2}]}") - .build())) - .withMessageStartingWith("Fields with the following paths were not found in the payload: [a[].b]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java deleted file mode 100644 index 6b98dac40..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/payload/XmlContentHandlerTests.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.payload; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; - -/** - * Tests for {@link XmlContentHandler}. - * - * @author Andy Wilkinson - */ -public class XmlContentHandlerTests { - - @Test - public void topLevelElementCanBeDocumented() { - List descriptors = Arrays.asList(fieldWithPath("a").type("a").description("description")); - String undocumentedContent = createHandler("5", descriptors).getUndocumentedContent(); - assertThat(undocumentedContent).isNull(); - } - - @Test - public void nestedElementCanBeDocumentedLeavingAncestors() { - List descriptors = Arrays.asList(fieldWithPath("a/b").type("b").description("description")); - String undocumentedContent = createHandler("5", descriptors).getUndocumentedContent(); - assertThat(undocumentedContent).isEqualTo(String.format("%n")); - } - - @Test - public void fieldDescriptorDoesNotDocumentEntireSubsection() { - List descriptors = Arrays.asList(fieldWithPath("a").type("a").description("description")); - String undocumentedContent = createHandler("5", descriptors).getUndocumentedContent(); - assertThat(undocumentedContent).isEqualTo(String.format("%n 5%n%n")); - } - - @Test - public void subsectionDescriptorDocumentsEntireSubsection() { - List descriptors = Arrays.asList(subsectionWithPath("a").type("a").description("description")); - String undocumentedContent = createHandler("5", descriptors).getUndocumentedContent(); - assertThat(undocumentedContent).isNull(); - } - - @Test - public void multipleElementsCanBeInDescendingOrderDocumented() { - List descriptors = Arrays.asList(fieldWithPath("a").type("a").description("description"), - fieldWithPath("a/b").type("b").description("description")); - String undocumentedContent = createHandler("5", descriptors).getUndocumentedContent(); - assertThat(undocumentedContent).isNull(); - } - - @Test - public void multipleElementsCanBeInAscendingOrderDocumented() { - List descriptors = Arrays.asList(fieldWithPath("a/b").type("b").description("description"), - fieldWithPath("a").type("a").description("description")); - String undocumentedContent = createHandler("5", descriptors).getUndocumentedContent(); - assertThat(undocumentedContent).isNull(); - } - - @Test - public void failsFastWithNonXmlContent() { - assertThatExceptionOfType(PayloadHandlingException.class) - .isThrownBy(() -> createHandler("non-XML content", Collections.emptyList())); - } - - private XmlContentHandler createHandler(String xml, List descriptors) { - return new XmlContentHandler(xml.getBytes(), descriptors); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java deleted file mode 100644 index 0cf383690..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/FormParametersSnippetTests.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link FormParametersSnippet}. - * - * @author Andy Wilkinson - */ -class FormParametersSnippetTests { - - @RenderedSnippetTest - void formParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new FormParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void formParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost").content("a=").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); - } - - @RenderedSnippetTest - void ignoredFormParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new FormParametersSnippet( - Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); - } - - @RenderedSnippetTest - void allUndocumentedFormParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new FormParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); - } - - @RenderedSnippetTest - void missingOptionalFormParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), - parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost").content("b=bravo").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void presentOptionalFormParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())) - .document(operationBuilder.request("http://localhost").content("a=alpha").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "form-parameters", template = "form-parameters-with-title") - void formParametersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new FormParametersSnippet( - Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), - parameterWithName("b").description("two").attributes(key("foo").value("bravo"))), - attributes(key("title").value("The title"))) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()).contains("The title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "form-parameters", template = "form-parameters-with-extra-column") - void formParametersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new FormParametersSnippet( - Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), - parameterWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()).isTable((table) -> table.withHeader("Parameter", "Description", "Foo") - .row("a", "one", "alpha") - .row("b", "two", "bravo")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "form-parameters", template = "form-parameters-with-optional-column") - void formParametersWithOptionalColumn(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), - parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Optional", "Description") - .row("a", "true", "one") - .row("b", "false", "two")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - RequestDocumentation.formParameters(parameterWithName("a").description("one")) - .and(parameterWithName("b").description("two")) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void additionalDescriptorsWithRelaxedFormParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - RequestDocumentation.relaxedFormParameters(parameterWithName("a").description("one")) - .and(parameterWithName("b").description("two")) - .document(operationBuilder.request("http://localhost").content("a=alpha&b=bravo&c=undocumented").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void formParametersWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - RequestDocumentation.formParameters(parameterWithName("Foo|Bar").description("one|two")) - .document(operationBuilder.request("http://localhost").content("Foo%7CBar=baz").build()); - assertThat(snippets.formParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void undocumentedParameter(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new FormParametersSnippet(Collections.emptyList()) - .document(operationBuilder.request("http://localhost").content("a=alpha").build())) - .withMessage("Form parameters with the following names were not documented: [a]"); - } - - @SnippetTest - void missingParameter(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost").build())) - .withMessage("Form parameters with the following names were not found in the request: [a]"); - } - - @SnippetTest - void undocumentedAndMissingParameters(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new FormParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost").content("b=bravo").build())) - .withMessage("Form parameters with the following names were not documented: [b]. Form parameters" - + " with the following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java deleted file mode 100644 index d178bde51..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/PathParametersSnippetTests.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link PathParametersSnippet}. - * - * @author Andy Wilkinson - */ -class PathParametersSnippetTests { - - @RenderedSnippetTest - void pathParameters(OperationBuilder operationBuilder, AssertableSnippets snippets, TemplateFormat templateFormat) - throws IOException { - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") - .row("`a`", "one") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void ignoredPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void allUndocumentedPathParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true).document( - operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}").build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void missingOptionalPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"), - parameterWithName("b").description("two").optional())) - .document( - operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}").build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat, "/{a}"), "Parameter", "Description") - .row("`a`", "one") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void presentOptionalPathParameter(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())).document( - operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}").build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat, "/{a}"), "Parameter", "Description") - .row("`a`", "one")); - } - - @RenderedSnippetTest - void pathParametersWithQueryString(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(operationBuilder - .attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}?foo=bar") - .build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") - .row("`a`", "one") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void pathParametersWithQueryStringWithParameters(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(operationBuilder - .attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}?foo={c}") - .build()); - assertThat(snippets.pathParameters()) - .isTable((table) -> table.withTitleAndHeader(getTitle(templateFormat), "Parameter", "Description") - .row("`a`", "one") - .row("`b`", "two")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "path-parameters", template = "path-parameters-with-title") - void pathParametersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), - parameterWithName("b").description("two").attributes(key("foo").value("bravo"))), - attributes(key("title").value("The title"))) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(snippets.pathParameters()).contains("The title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "path-parameters", template = "path-parameters-with-extra-column") - void pathParametersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - new PathParametersSnippet( - Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), - parameterWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(snippets.pathParameters()).isTable((table) -> table.withHeader("Parameter", "Description", "Foo") - .row("a", "one", "alpha") - .row("b", "two", "bravo")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - RequestDocumentation.pathParameters(parameterWithName("a").description("one")) - .and(parameterWithName("b").description("two")) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}") - .build()); - assertThat(snippets.pathParameters()).isTable( - (table) -> table.withTitleAndHeader(getTitle(templateFormat, "/{a}/{b}"), "Parameter", "Description") - .row("`a`", "one") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void additionalDescriptorsWithRelaxedRequestParameters(OperationBuilder operationBuilder, - AssertableSnippets snippets, TemplateFormat templateFormat) throws IOException { - RequestDocumentation.relaxedPathParameters(parameterWithName("a").description("one")) - .and(parameterWithName("b").description("two")) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/{b}/{c}") - .build()); - assertThat(snippets.pathParameters()).isTable((table) -> table - .withTitleAndHeader(getTitle(templateFormat, "/{a}/{b}/{c}"), "Parameter", "Description") - .row("`a`", "one") - .row("`b`", "two")); - } - - @RenderedSnippetTest - void pathParametersWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets, - TemplateFormat templateFormat) throws IOException { - RequestDocumentation.pathParameters(parameterWithName("Foo|Bar").description("one|two")) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{Foo|Bar}") - .build()); - assertThat(snippets.pathParameters()).isTable( - (table) -> table.withTitleAndHeader(getTitle(templateFormat, "{Foo|Bar}"), "Parameter", "Description") - .row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void undocumentedPathParameter(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new PathParametersSnippet(Collections.emptyList()) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{a}/") - .build())) - .withMessage("Path parameters with the following names were not documented: [a]"); - } - - @SnippetTest - void missingPathParameter(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/") - .build())) - .withMessage("Path parameters with the following names were not found in the request: [a]"); - } - - @SnippetTest - void undocumentedAndMissingPathParameters(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.attribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "/{b}") - .build())) - .withMessage("Path parameters with the following names were not documented: [b]. Path parameters with the" - + " following names were not found in the request: [a]"); - } - - private String getTitle(TemplateFormat templateFormat) { - return getTitle(templateFormat, "/{a}/{b}"); - } - - private String getTitle(TemplateFormat templateFormat, String title) { - if (templateFormat.getId().equals(TemplateFormats.asciidoctor().getId())) { - return "+" + title + "+"; - } - return "`" + title + "`"; - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java deleted file mode 100644 index 8084a2382..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/QueryParametersSnippetTests.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link QueryParametersSnippet}. - * - * @author Andy Wilkinson - */ -class QueryParametersSnippetTests { - - @RenderedSnippetTest - void queryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new QueryParametersSnippet( - Arrays.asList(parameterWithName("a").description("one"), parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void queryParameterWithNoValue(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost?a").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); - } - - @RenderedSnippetTest - void ignoredQueryParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new QueryParametersSnippet( - Arrays.asList(parameterWithName("a").ignored(), parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); - } - - @RenderedSnippetTest - void allUndocumentedQueryParametersCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new QueryParametersSnippet(Arrays.asList(parameterWithName("b").description("two")), true) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`b`", "two")); - } - - @RenderedSnippetTest - void missingOptionalQueryParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), - parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost?b=bravo").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void presentOptionalQueryParameter(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional())) - .document(operationBuilder.request("http://localhost?a=alpha").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "query-parameters", template = "query-parameters-with-title") - void queryParametersWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new QueryParametersSnippet( - Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), - parameterWithName("b").description("two").attributes(key("foo").value("bravo"))), - attributes(key("title").value("The title"))) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()).contains("The title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "query-parameters", template = "query-parameters-with-extra-column") - void queryParametersWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new QueryParametersSnippet( - Arrays.asList(parameterWithName("a").description("one").attributes(key("foo").value("alpha")), - parameterWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()).isTable((table) -> table.withHeader("Parameter", "Description", "Foo") - .row("a", "one", "alpha") - .row("b", "two", "bravo")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "query-parameters", template = "query-parameters-with-optional-column") - void queryParametersWithOptionalColumn(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one").optional(), - parameterWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Optional", "Description") - .row("a", "true", "one") - .row("b", "false", "two")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - RequestDocumentation.queryParameters(parameterWithName("a").description("one")) - .and(parameterWithName("b").description("two")) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void additionalDescriptorsWithRelaxedQueryParameters(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - RequestDocumentation.relaxedQueryParameters(parameterWithName("a").description("one")) - .and(parameterWithName("b").description("two")) - .document(operationBuilder.request("http://localhost?a=alpha&b=bravo&c=undocumented").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void queryParametersWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - RequestDocumentation.queryParameters(parameterWithName("Foo|Bar").description("one|two")) - .document(operationBuilder.request("http://localhost?Foo%7CBar=baz").build()); - assertThat(snippets.queryParameters()) - .isTable((table) -> table.withHeader("Parameter", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void undocumentedParameter(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new QueryParametersSnippet(Collections.emptyList()) - .document(operationBuilder.request("http://localhost?a=alpha").build())) - .withMessage("Query parameters with the following names were not documented: [a]"); - } - - @SnippetTest - void missingParameter(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost").build())) - .withMessage("Query parameters with the following names were not found in the request: [a]"); - } - - @SnippetTest - void undocumentedAndMissingParameters(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new QueryParametersSnippet(Arrays.asList(parameterWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost?b=bravo").build())) - .withMessage("Query parameters with the following names were not documented: [b]. Query parameters" - + " with the following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java deleted file mode 100644 index 9dcc12f9f..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/request/RequestPartsSnippetTests.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.request; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; - -import org.springframework.restdocs.snippet.SnippetException; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTemplate; -import org.springframework.restdocs.testfixtures.jupiter.SnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.snippet.Attributes.attributes; -import static org.springframework.restdocs.snippet.Attributes.key; - -/** - * Tests for {@link RequestPartsSnippet}. - * - * @author Andy Wilkinson - */ -class RequestPartsSnippetTests { - - @RenderedSnippetTest - void requestParts(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestPartsSnippet( - Arrays.asList(partWithName("a").description("one"), partWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost") - .part("a", "bravo".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void ignoredRequestPart(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestPartsSnippet(Arrays.asList(partWithName("a").ignored(), partWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost") - .part("a", "bravo".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`b`", "two")); - } - - @RenderedSnippetTest - void allUndocumentedRequestPartsCanBeIgnored(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestPartsSnippet(Arrays.asList(partWithName("b").description("two")), true) - .document(operationBuilder.request("http://localhost") - .part("a", "bravo".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`b`", "two")); - } - - @RenderedSnippetTest - void missingOptionalRequestPart(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestPartsSnippet( - Arrays.asList(partWithName("a").description("one").optional(), partWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost").part("b", "bravo".getBytes()).build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void presentOptionalRequestPart(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one").optional())) - .document(operationBuilder.request("http://localhost").part("a", "one".getBytes()).build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-parts", template = "request-parts-with-title") - void requestPartsWithCustomAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestPartsSnippet( - Arrays.asList(partWithName("a").description("one").attributes(key("foo").value("alpha")), - partWithName("b").description("two").attributes(key("foo").value("bravo"))), - attributes(key("title").value("The title"))) - .document(operationBuilder.request("http://localhost") - .part("a", "alpha".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()).contains("The title"); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-parts", template = "request-parts-with-extra-column") - void requestPartsWithCustomDescriptorAttributes(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestPartsSnippet( - Arrays.asList(partWithName("a").description("one").attributes(key("foo").value("alpha")), - partWithName("b").description("two").attributes(key("foo").value("bravo")))) - .document(operationBuilder.request("http://localhost") - .part("a", "alpha".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()).isTable((table) -> table.withHeader("Part", "Description", "Foo") - .row("a", "one", "alpha") - .row("b", "two", "bravo")); - } - - @RenderedSnippetTest - @SnippetTemplate(snippet = "request-parts", template = "request-parts-with-optional-column") - void requestPartsWithOptionalColumn(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - new RequestPartsSnippet( - Arrays.asList(partWithName("a").description("one").optional(), partWithName("b").description("two"))) - .document(operationBuilder.request("http://localhost") - .part("a", "alpha".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()).isTable((table) -> table.withHeader("Part", "Optional", "Description") - .row("a", "true", "one") - .row("b", "false", "two")); - } - - @RenderedSnippetTest - void additionalDescriptors(OperationBuilder operationBuilder, AssertableSnippets snippets) throws IOException { - RequestDocumentation.requestParts(partWithName("a").description("one")) - .and(partWithName("b").description("two")) - .document(operationBuilder.request("http://localhost") - .part("a", "bravo".getBytes()) - .and() - .part("b", "bravo".getBytes()) - .build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`a`", "one").row("`b`", "two")); - } - - @RenderedSnippetTest - void requestPartsWithEscapedContent(OperationBuilder operationBuilder, AssertableSnippets snippets) - throws IOException { - RequestDocumentation.requestParts(partWithName("Foo|Bar").description("one|two")) - .document(operationBuilder.request("http://localhost").part("Foo|Bar", "baz".getBytes()).build()); - assertThat(snippets.requestParts()) - .isTable((table) -> table.withHeader("Part", "Description").row("`Foo|Bar`", "one|two")); - } - - @SnippetTest - void undocumentedPart(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartsSnippet(Collections.emptyList()) - .document(operationBuilder.request("http://localhost").part("a", "alpha".getBytes()).build())) - .withMessage("Request parts with the following names were not documented: [a]"); - } - - @SnippetTest - void missingPart(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost").build())) - .withMessage("Request parts with the following names were not found in the request: [a]"); - } - - @SnippetTest - void undocumentedAndMissingParts(OperationBuilder operationBuilder) { - assertThatExceptionOfType(SnippetException.class) - .isThrownBy(() -> new RequestPartsSnippet(Arrays.asList(partWithName("a").description("one"))) - .document(operationBuilder.request("http://localhost").part("b", "bravo".getBytes()).build())) - .withMessage("Request parts with the following names were not documented: [b]. Request parts with the" - + " following names were not found in the request: [a]"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java deleted file mode 100644 index 22b285c30..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/RestDocumentationContextPlaceholderResolverTests.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.ManualRestDocumentation; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link RestDocumentationContextPlaceholderResolver}. - * - * @author Andy Wilkinson - * - */ -class RestDocumentationContextPlaceholderResolverTests { - - @Test - void kebabCaseMethodName() { - assertThat(createResolver("dashSeparatedMethodName").resolvePlaceholder("method-name")) - .isEqualTo("dash-separated-method-name"); - } - - @Test - void kebabCaseMethodNameWithUpperCaseOpeningSection() { - assertThat(createResolver("URIDashSeparatedMethodName").resolvePlaceholder("method-name")) - .isEqualTo("uri-dash-separated-method-name"); - } - - @Test - void kebabCaseMethodNameWithUpperCaseMidSection() { - assertThat(createResolver("dashSeparatedMethodNameWithURIInIt").resolvePlaceholder("method-name")) - .isEqualTo("dash-separated-method-name-with-uri-in-it"); - } - - @Test - void kebabCaseMethodNameWithUpperCaseEndSection() { - assertThat(createResolver("dashSeparatedMethodNameWithURI").resolvePlaceholder("method-name")) - .isEqualTo("dash-separated-method-name-with-uri"); - } - - @Test - void snakeCaseMethodName() { - assertThat(createResolver("underscoreSeparatedMethodName").resolvePlaceholder("method_name")) - .isEqualTo("underscore_separated_method_name"); - } - - @Test - void snakeCaseMethodNameWithUpperCaseOpeningSection() { - assertThat(createResolver("URIUnderscoreSeparatedMethodName").resolvePlaceholder("method_name")) - .isEqualTo("uri_underscore_separated_method_name"); - } - - @Test - void snakeCaseMethodNameWithUpperCaseMidSection() { - assertThat(createResolver("underscoreSeparatedMethodNameWithURIInIt").resolvePlaceholder("method_name")) - .isEqualTo("underscore_separated_method_name_with_uri_in_it"); - } - - @Test - void snakeCaseMethodNameWithUpperCaseEndSection() { - assertThat(createResolver("underscoreSeparatedMethodNameWithURI").resolvePlaceholder("method_name")) - .isEqualTo("underscore_separated_method_name_with_uri"); - } - - @Test - void camelCaseMethodName() { - assertThat(createResolver("camelCaseMethodName").resolvePlaceholder("methodName")) - .isEqualTo("camelCaseMethodName"); - } - - @Test - void kebabCaseClassName() { - assertThat(createResolver().resolvePlaceholder("class-name")) - .isEqualTo("rest-documentation-context-placeholder-resolver-tests"); - } - - @Test - void snakeCaseClassName() { - assertThat(createResolver().resolvePlaceholder("class_name")) - .isEqualTo("rest_documentation_context_placeholder_resolver_tests"); - } - - @Test - void camelCaseClassName() { - assertThat(createResolver().resolvePlaceholder("ClassName")) - .isEqualTo("RestDocumentationContextPlaceholderResolverTests"); - } - - @Test - void stepCount() { - assertThat(createResolver("stepCount").resolvePlaceholder("step")).isEqualTo("1"); - } - - private PlaceholderResolver createResolver() { - return createResolver(null); - } - - private PlaceholderResolver createResolver(String methodName) { - return new RestDocumentationContextPlaceholderResolver(createContext(methodName)); - } - - private RestDocumentationContext createContext(String methodName) { - ManualRestDocumentation manualRestDocumentation = new ManualRestDocumentation("build"); - manualRestDocumentation.beforeTest(getClass(), methodName); - RestDocumentationContext context = manualRestDocumentation.beforeOperation(); - return context; - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java deleted file mode 100644 index 0df5118d1..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/StandardWriterResolverTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.Writer; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import org.springframework.restdocs.ManualRestDocumentation; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link StandardWriterResolver}. - * - * @author Andy Wilkinson - */ -class StandardWriterResolverTests { - - @TempDir - File temp; - - private final PlaceholderResolverFactory placeholderResolverFactory = mock(PlaceholderResolverFactory.class); - - private final StandardWriterResolver resolver = new StandardWriterResolver(this.placeholderResolverFactory, "UTF-8", - TemplateFormats.asciidoctor()); - - @Test - void absoluteInput() { - String absolutePath = new File("foo").getAbsolutePath(); - assertThat(this.resolver.resolveFile(absolutePath, "bar.txt", createContext(absolutePath))) - .isEqualTo(new File(absolutePath, "bar.txt")); - } - - @Test - void configuredOutputAndRelativeInput() { - File outputDir = new File("foo").getAbsoluteFile(); - assertThat(this.resolver.resolveFile("bar", "baz.txt", createContext(outputDir.getAbsolutePath()))) - .isEqualTo(new File(outputDir, "bar/baz.txt")); - } - - @Test - void configuredOutputAndAbsoluteInput() { - File outputDir = new File("foo").getAbsoluteFile(); - String absolutePath = new File("bar").getAbsolutePath(); - assertThat(this.resolver.resolveFile(absolutePath, "baz.txt", createContext(outputDir.getAbsolutePath()))) - .isEqualTo(new File(absolutePath, "baz.txt")); - } - - @Test - void placeholdersAreResolvedInOperationName() throws IOException { - File outputDirectory = this.temp; - RestDocumentationContext context = createContext(outputDirectory.getAbsolutePath()); - PlaceholderResolver resolver = mock(PlaceholderResolver.class); - given(resolver.resolvePlaceholder("a")).willReturn("alpha"); - given(this.placeholderResolverFactory.create(context)).willReturn(resolver); - try (Writer writer = this.resolver.resolve("{a}", "bravo", context)) { - assertSnippetLocation(writer, new File(outputDirectory, "alpha/bravo.adoc")); - } - } - - @Test - void placeholdersAreResolvedInSnippetName() throws IOException { - File outputDirectory = this.temp; - RestDocumentationContext context = createContext(outputDirectory.getAbsolutePath()); - PlaceholderResolver resolver = mock(PlaceholderResolver.class); - given(resolver.resolvePlaceholder("b")).willReturn("bravo"); - given(this.placeholderResolverFactory.create(context)).willReturn(resolver); - try (Writer writer = this.resolver.resolve("alpha", "{b}", context)) { - assertSnippetLocation(writer, new File(outputDirectory, "alpha/bravo.adoc")); - } - } - - private RestDocumentationContext createContext(String outputDir) { - ManualRestDocumentation manualRestDocumentation = new ManualRestDocumentation(outputDir); - manualRestDocumentation.beforeTest(getClass(), null); - RestDocumentationContext context = manualRestDocumentation.beforeOperation(); - return context; - } - - private void assertSnippetLocation(Writer writer, File expectedLocation) throws IOException { - writer.write("test"); - writer.flush(); - assertThat(expectedLocation).exists(); - assertThat(FileCopyUtils.copyToString(new FileReader(expectedLocation))).isEqualTo("test"); - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java deleted file mode 100644 index 724e68a5d..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/snippet/TemplatedSnippetTests.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.snippet; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.testfixtures.jupiter.AssertableSnippets; -import org.springframework.restdocs.testfixtures.jupiter.OperationBuilder; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link TemplatedSnippet}. - * - * @author Andy Wilkinson - */ -class TemplatedSnippetTests { - - @Test - void attributesAreCopied() { - Map attributes = new HashMap<>(); - attributes.put("a", "alpha"); - TemplatedSnippet snippet = new TestTemplatedSnippet(attributes); - attributes.put("b", "bravo"); - assertThat(snippet.getAttributes()).hasSize(1); - assertThat(snippet.getAttributes()).containsEntry("a", "alpha"); - } - - @Test - void nullAttributesAreTolerated() { - assertThat(new TestTemplatedSnippet(null).getAttributes()).isNotNull(); - assertThat(new TestTemplatedSnippet(null).getAttributes()).isEmpty(); - } - - @Test - void snippetName() { - assertThat(new TestTemplatedSnippet(Collections.emptyMap()).getSnippetName()).isEqualTo("test"); - } - - @RenderedSnippetTest - void multipleSnippetsCanBeProducedFromTheSameTemplate(OperationBuilder operationBuilder, AssertableSnippets snippet) - throws IOException { - new TestTemplatedSnippet("one", "multiple-snippets").document(operationBuilder.build()); - new TestTemplatedSnippet("two", "multiple-snippets").document(operationBuilder.build()); - assertThat(snippet.named("multiple-snippets-one")).exists(); - assertThat(snippet.named("multiple-snippets-two")).exists(); - } - - private static class TestTemplatedSnippet extends TemplatedSnippet { - - protected TestTemplatedSnippet(String snippetName, String templateName) { - super(templateName + "-" + snippetName, templateName, Collections.emptyMap()); - } - - protected TestTemplatedSnippet(Map attributes) { - super("test", attributes); - } - - @Override - protected Map createModel(Operation operation) { - return new HashMap<>(); - } - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java deleted file mode 100644 index 4f5646337..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/StandardTemplateResourceResolverTests.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates; - -import java.io.IOException; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Callable; - -import org.junit.jupiter.api.Test; - -import org.springframework.core.io.Resource; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; - -/** - * Tests for {@link TemplateResourceResolver}. - * - * @author Andy Wilkinson - */ -class StandardTemplateResourceResolverTests { - - private final TemplateResourceResolver resolver = new StandardTemplateResourceResolver( - TemplateFormats.asciidoctor()); - - private final TestClassLoader classLoader = new TestClassLoader(); - - @Test - void formatSpecificCustomSnippetHasHighestPrecedence() throws IOException { - this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/test.snippet", - getClass().getResource("test-format-specific-custom.snippet")); - this.classLoader.addResource("org/springframework/restdocs/templates/test.snippet", - getClass().getResource("test-custom.snippet")); - this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/default-test.snippet", - getClass().getResource("test-default.snippet")); - Resource snippet = doWithThreadContextClassLoader(this.classLoader, new Callable() { - - @Override - public Resource call() { - return StandardTemplateResourceResolverTests.this.resolver.resolveTemplateResource("test"); - } - - }); - - assertThat(snippet.getURL()).isEqualTo(getClass().getResource("test-format-specific-custom.snippet")); - } - - @Test - void generalCustomSnippetIsUsedInAbsenceOfFormatSpecificCustomSnippet() throws IOException { - this.classLoader.addResource("org/springframework/restdocs/templates/test.snippet", - getClass().getResource("test-custom.snippet")); - this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/default-test.snippet", - getClass().getResource("test-default.snippet")); - Resource snippet = doWithThreadContextClassLoader(this.classLoader, new Callable() { - - @Override - public Resource call() { - return StandardTemplateResourceResolverTests.this.resolver.resolveTemplateResource("test"); - } - - }); - - assertThat(snippet.getURL()).isEqualTo(getClass().getResource("test-custom.snippet")); - } - - @Test - void defaultSnippetIsUsedInAbsenceOfCustomSnippets() throws Exception { - this.classLoader.addResource("org/springframework/restdocs/templates/asciidoctor/default-test.snippet", - getClass().getResource("test-default.snippet")); - Resource snippet = doWithThreadContextClassLoader(this.classLoader, new Callable() { - - @Override - public Resource call() { - return StandardTemplateResourceResolverTests.this.resolver.resolveTemplateResource("test"); - } - - }); - - assertThat(snippet.getURL()).isEqualTo(getClass().getResource("test-default.snippet")); - } - - @Test - void failsIfCustomAndDefaultSnippetsDoNotExist() { - assertThatIllegalStateException() - .isThrownBy(() -> doWithThreadContextClassLoader(this.classLoader, - () -> StandardTemplateResourceResolverTests.this.resolver.resolveTemplateResource("test"))) - .withMessage("Template named 'test' could not be resolved"); - } - - private T doWithThreadContextClassLoader(ClassLoader classLoader, Callable action) { - ClassLoader previous = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - try { - return action.call(); - } - catch (Exception ex) { - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - throw new RuntimeException(ex); - } - finally { - Thread.currentThread().setContextClassLoader(previous); - } - } - - private static final class TestClassLoader extends ClassLoader { - - private Map resources = new HashMap<>(); - - private void addResource(String name, URL url) { - this.resources.put(name, url); - } - - @Override - public URL getResource(String name) { - return this.resources.get(name); - } - - } - -} diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java deleted file mode 100644 index 635a80ae5..000000000 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/templates/mustache/AsciidoctorTableCellContentLambdaTests.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.templates.mustache; - -import java.io.IOException; -import java.io.StringWriter; - -import org.junit.jupiter.api.Test; - -import org.springframework.restdocs.mustache.Template.Fragment; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link AsciidoctorTableCellContentLambda}. - * - * @author Andy Wilkinson - */ -class AsciidoctorTableCellContentLambdaTests { - - @Test - void verticalBarCharactersAreEscaped() throws IOException { - Fragment fragment = mock(Fragment.class); - given(fragment.execute()).willReturn("|foo|bar|baz|"); - StringWriter writer = new StringWriter(); - new AsciidoctorTableCellContentLambda().execute(fragment, writer); - assertThat(writer.toString()).isEqualTo("\\|foo\\|bar\\|baz\\|"); - } - - @Test - void escapedVerticalBarCharactersAreNotEscapedAgain() throws IOException { - Fragment fragment = mock(Fragment.class); - given(fragment.execute()).willReturn("\\|foo|bar\\|baz|"); - StringWriter writer = new StringWriter(); - new AsciidoctorTableCellContentLambda().execute(fragment, writer); - assertThat(writer.toString()).isEqualTo("\\|foo\\|bar\\|baz\\|"); - } - -} diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/curl-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/curl-request-with-title.snippet deleted file mode 100644 index fa5ee3f4b..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/curl-request-with-title.snippet +++ /dev/null @@ -1,5 +0,0 @@ -[source,bash] -.{{title}} ----- -$ curl {{url}} {{options}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-extra-column.snippet deleted file mode 100644 index fb97f89bf..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Parameter|Description|Foo - -{{#parameters}} -|{{name}} -|{{description}} -|{{foo}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-optional-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-optional-column.snippet deleted file mode 100644 index 70847ba1d..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-optional-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Parameter|Optional|Description - -{{#parameters}} -|{{name}} -|{{optional}} -|{{description}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-title.snippet deleted file mode 100644 index 611254aa9..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/form-parameters-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Parameter|Description - -{{#parameters}} -|{{name}} -|{{description}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/http-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/http-request-with-title.snippet deleted file mode 100644 index 812b2e3ff..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/http-request-with-title.snippet +++ /dev/null @@ -1,9 +0,0 @@ -[source,http] -.{{title}} ----- -{{method}} {{path}} HTTP/1.1 -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{requestBody}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/http-response-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/http-response-with-title.snippet deleted file mode 100644 index 10931202c..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/http-response-with-title.snippet +++ /dev/null @@ -1,9 +0,0 @@ -[source,http] -.{{title}} ----- -HTTP/1.1 {{statusCode}} {{statusReason}} -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{responseBody}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/httpie-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/httpie-request-with-title.snippet deleted file mode 100644 index 7c4eb0483..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/httpie-request-with-title.snippet +++ /dev/null @@ -1,5 +0,0 @@ -[source,bash] -.{{title}} ----- -$ {{echoContent}}http {{options}} {{url}}{{requestItems}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/links-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/links-with-extra-column.snippet deleted file mode 100644 index 81c8232c9..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/links-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Relation|Description|Foo - -{{#links}} -|{{rel}} -|{{description}} -|{{foo}} - -{{/links}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/links-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/links-with-title.snippet deleted file mode 100644 index ea4aa06d5..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/links-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Relation|Description - -{{#links}} -|{{rel}} -|{{description}} - -{{/links}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/path-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/path-parameters-with-extra-column.snippet deleted file mode 100644 index fb97f89bf..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/path-parameters-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Parameter|Description|Foo - -{{#parameters}} -|{{name}} -|{{description}} -|{{foo}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/path-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/path-parameters-with-title.snippet deleted file mode 100644 index 611254aa9..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/path-parameters-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Parameter|Description - -{{#parameters}} -|{{name}} -|{{description}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-extra-column.snippet deleted file mode 100644 index fb97f89bf..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Parameter|Description|Foo - -{{#parameters}} -|{{name}} -|{{description}} -|{{foo}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-optional-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-optional-column.snippet deleted file mode 100644 index 70847ba1d..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-optional-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Parameter|Optional|Description - -{{#parameters}} -|{{name}} -|{{optional}} -|{{description}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-title.snippet deleted file mode 100644 index 611254aa9..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/query-parameters-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Parameter|Description - -{{#parameters}} -|{{name}} -|{{description}} - -{{/parameters}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-body-with-language.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-body-with-language.snippet deleted file mode 100644 index 0429ee575..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-body-with-language.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source,{{language}},options="nowrap"] ----- -{{body}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-cookies-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-cookies-with-extra-column.snippet deleted file mode 100644 index 62c9dad68..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-cookies-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Name|Description|Foo - -{{#cookies}} -|{{name}} -|{{description}} -|{{foo}} - -{{/cookies}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-cookies-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-cookies-with-title.snippet deleted file mode 100644 index 5e4a4af43..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-cookies-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Name|Description - -{{#cookies}} -|{{name}} -|{{description}} - -{{/cookies}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-extra-column.snippet deleted file mode 100644 index 4830d8956..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-extra-column.snippet +++ /dev/null @@ -1,11 +0,0 @@ -|=== -|Path|Type|Description|Foo - -{{#fields}} -|{{path}} -|{{type}} -|{{description}} -|{{foo}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-list-description.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-list-description.snippet deleted file mode 100644 index e48204967..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-list-description.snippet +++ /dev/null @@ -1,12 +0,0 @@ -[cols="1,1,1a"] -|=== -|Path|Type|Description - -{{#fields}} -|{{path}} -|{{type}} -|{{#description}} - {{.}} -{{/description}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-title.snippet deleted file mode 100644 index a51b74aee..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-fields-with-title.snippet +++ /dev/null @@ -1,11 +0,0 @@ -.{{title}} -|=== -|Path|Type|Description - -{{#fields}} -|{{path}} -|{{type}} -|{{description}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-headers-with-extra-column.snippet deleted file mode 100644 index 29d687777..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-headers-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Name|Description|Foo - -{{#headers}} -|{{name}} -|{{description}} -|{{foo}} - -{{/headers}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-headers-with-title.snippet deleted file mode 100644 index 998090a0d..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-headers-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Name|Description - -{{#headers}} -|{{name}} -|{{description}} - -{{/headers}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-part-body-with-language.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-part-body-with-language.snippet deleted file mode 100644 index 0429ee575..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-part-body-with-language.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source,{{language}},options="nowrap"] ----- -{{body}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-extra-column.snippet deleted file mode 100644 index 95f52f2fd..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Part|Description|Foo - -{{#requestParts}} -|{{name}} -|{{description}} -|{{foo}} - -{{/requestParts}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-optional-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-optional-column.snippet deleted file mode 100644 index ca0234649..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-optional-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Part|Optional|Description - -{{#requestParts}} -|{{name}} -|{{optional}} -|{{description}} - -{{/requestParts}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-title.snippet deleted file mode 100644 index 7e6731826..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/request-parts-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Part|Description - -{{#requestParts}} -|{{name}} -|{{description}} - -{{/requestParts}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-body-with-language.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-body-with-language.snippet deleted file mode 100644 index 0429ee575..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-body-with-language.snippet +++ /dev/null @@ -1,4 +0,0 @@ -[source,{{language}},options="nowrap"] ----- -{{body}} ----- \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-cookies-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-cookies-with-extra-column.snippet deleted file mode 100644 index 62c9dad68..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-cookies-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Name|Description|Foo - -{{#cookies}} -|{{name}} -|{{description}} -|{{foo}} - -{{/cookies}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-cookies-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-cookies-with-title.snippet deleted file mode 100644 index 5e4a4af43..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-cookies-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Name|Description - -{{#cookies}} -|{{name}} -|{{description}} - -{{/cookies}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-fields-with-extra-column.snippet deleted file mode 100644 index 4830d8956..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-fields-with-extra-column.snippet +++ /dev/null @@ -1,11 +0,0 @@ -|=== -|Path|Type|Description|Foo - -{{#fields}} -|{{path}} -|{{type}} -|{{description}} -|{{foo}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-fields-with-title.snippet deleted file mode 100644 index a51b74aee..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-fields-with-title.snippet +++ /dev/null @@ -1,11 +0,0 @@ -.{{title}} -|=== -|Path|Type|Description - -{{#fields}} -|{{path}} -|{{type}} -|{{description}} - -{{/fields}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-headers-with-extra-column.snippet deleted file mode 100644 index 29d687777..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-headers-with-extra-column.snippet +++ /dev/null @@ -1,10 +0,0 @@ -|=== -|Name|Description|Foo - -{{#headers}} -|{{name}} -|{{description}} -|{{foo}} - -{{/headers}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-headers-with-title.snippet deleted file mode 100644 index 998090a0d..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/asciidoctor/response-headers-with-title.snippet +++ /dev/null @@ -1,10 +0,0 @@ -.{{title}} -|=== -|Name|Description - -{{#headers}} -|{{name}} -|{{description}} - -{{/headers}} -|=== \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/curl-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/curl-request-with-title.snippet deleted file mode 100644 index 483ab6c7f..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/curl-request-with-title.snippet +++ /dev/null @@ -1,4 +0,0 @@ -{{title}} -```bash -$ curl {{url}} {{options}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-extra-column.snippet deleted file mode 100644 index 260502b69..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Description | Foo ---------- | ----------- | --- -{{#parameters}} -{{name}} | {{description}} | {{foo}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-optional-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-optional-column.snippet deleted file mode 100644 index 2f9cd0774..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-optional-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Optional | Description ---------- | -------- | ----------- -{{#parameters}} -{{name}} | {{optional}} | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-title.snippet deleted file mode 100644 index b63b62353..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/form-parameters-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Parameter | Description ---------- | ----------- -{{#parameters}} -{{name}} | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/http-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/http-request-with-title.snippet deleted file mode 100644 index 363a93694..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/http-request-with-title.snippet +++ /dev/null @@ -1,8 +0,0 @@ -{{title}} -```http -{{method}} {{path}} HTTP/1.1 -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{requestBody}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/http-response-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/http-response-with-title.snippet deleted file mode 100644 index 103fa6dcb..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/http-response-with-title.snippet +++ /dev/null @@ -1,8 +0,0 @@ -{{title}} -```http -HTTP/1.1 {{statusCode}} {{statusReason}} -{{#headers}} -{{name}}: {{value}} -{{/headers}} -{{responseBody}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/httpie-request-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/httpie-request-with-title.snippet deleted file mode 100644 index 67dff4550..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/httpie-request-with-title.snippet +++ /dev/null @@ -1,4 +0,0 @@ -{{title}} -```bash -$ {{echoContent}}http {{options}} {{url}}{{requestItems}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/links-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/links-with-extra-column.snippet deleted file mode 100644 index a7e3349d3..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/links-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Relation | Description | Foo --------- | ----------- | --- -{{#links}} -{{rel}} | {{description}} | {{foo}} -{{/links}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/links-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/links-with-title.snippet deleted file mode 100644 index ecaaeb33b..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/links-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Relation | Description --------- | ----------- -{{#links}} -{{rel}} | {{description}} -{{/links}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/path-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/path-parameters-with-extra-column.snippet deleted file mode 100644 index 260502b69..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/path-parameters-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Description | Foo ---------- | ----------- | --- -{{#parameters}} -{{name}} | {{description}} | {{foo}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/path-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/path-parameters-with-title.snippet deleted file mode 100644 index b63b62353..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/path-parameters-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Parameter | Description ---------- | ----------- -{{#parameters}} -{{name}} | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-extra-column.snippet deleted file mode 100644 index 260502b69..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Description | Foo ---------- | ----------- | --- -{{#parameters}} -{{name}} | {{description}} | {{foo}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-optional-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-optional-column.snippet deleted file mode 100644 index 2f9cd0774..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-optional-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Parameter | Optional | Description ---------- | -------- | ----------- -{{#parameters}} -{{name}} | {{optional}} | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-title.snippet deleted file mode 100644 index b63b62353..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/query-parameters-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Parameter | Description ---------- | ----------- -{{#parameters}} -{{name}} | {{description}} -{{/parameters}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-body-with-language.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-body-with-language.snippet deleted file mode 100644 index f81732e96..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-body-with-language.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```{{language}} -{{body}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-cookies-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-cookies-with-extra-column.snippet deleted file mode 100644 index 8c7416287..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-cookies-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description | Foo ----- | ----------- | --- -{{#cookies}} -{{name}} | {{description}} | {{foo}} -{{/cookies}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-cookies-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-cookies-with-title.snippet deleted file mode 100644 index e5e2c8bda..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-cookies-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Name | Description ----- | ----------- -{{#cookies}} -{{name}} | {{description}} -{{/cookies}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-extra-column.snippet deleted file mode 100644 index e4ec4c605..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Path | Type | Description | Foo ----- | ---- | ----------- | --- -{{#fields}} -{{path}} | {{type}} | {{description}} | {{foo}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet deleted file mode 100644 index ff141ad37..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-fields-with-title.snippet +++ /dev/null @@ -1,7 +0,0 @@ -{{title}} - -Path | Type | Description ----- | ---- | ----------- -{{#fields}} -{{path}} | {{type}} | {{description}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-headers-with-extra-column.snippet deleted file mode 100644 index af392f2a8..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-headers-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description | Foo ----- | ----------- | --- -{{#headers}} -{{name}} | {{description}} | {{foo}} -{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-headers-with-title.snippet deleted file mode 100644 index d57ed7ba8..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-headers-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Name | Description ----- | ----------- -{{#headers}} -{{name}} | {{description}} -{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-part-body-with-language.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-part-body-with-language.snippet deleted file mode 100644 index f81732e96..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-part-body-with-language.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```{{language}} -{{body}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-extra-column.snippet deleted file mode 100644 index b81a83961..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Part | Description | Foo ----- | ----------- | --- -{{#requestParts}} -{{name}} | {{description}} | {{foo}} -{{/requestParts}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-optional-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-optional-column.snippet deleted file mode 100644 index c92cdc893..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-optional-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Part | Optional | Description ----- | -------- | ----------- -{{#requestParts}} -{{name}} | {{optional}} | {{description}} -{{/requestParts}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-title.snippet deleted file mode 100644 index 0dc1b3e9f..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/request-parts-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Part | Description ----- | ----------- -{{#requestParts}} -{{name}} | {{description}} -{{/requestParts}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-body-with-language.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-body-with-language.snippet deleted file mode 100644 index f81732e96..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-body-with-language.snippet +++ /dev/null @@ -1,3 +0,0 @@ -```{{language}} -{{body}} -``` \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-cookies-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-cookies-with-extra-column.snippet deleted file mode 100644 index 8c7416287..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-cookies-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description | Foo ----- | ----------- | --- -{{#cookies}} -{{name}} | {{description}} | {{foo}} -{{/cookies}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-cookies-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-cookies-with-title.snippet deleted file mode 100644 index e5e2c8bda..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-cookies-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Name | Description ----- | ----------- -{{#cookies}} -{{name}} | {{description}} -{{/cookies}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-fields-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-fields-with-extra-column.snippet deleted file mode 100644 index e4ec4c605..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-fields-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Path | Type | Description | Foo ----- | ---- | ----------- | --- -{{#fields}} -{{path}} | {{type}} | {{description}} | {{foo}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-fields-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-fields-with-title.snippet deleted file mode 100644 index 24bb63fa9..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-fields-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Path | Type | Description ----- | ---- | ----------- -{{#fields}} -{{path}} | {{type}} | {{description}} -{{/fields}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-headers-with-extra-column.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-headers-with-extra-column.snippet deleted file mode 100644 index af392f2a8..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-headers-with-extra-column.snippet +++ /dev/null @@ -1,5 +0,0 @@ -Name | Description | Foo ----- | ----------- | --- -{{#headers}} -{{name}} | {{description}} | {{foo}} -{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-headers-with-title.snippet b/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-headers-with-title.snippet deleted file mode 100644 index d57ed7ba8..000000000 --- a/spring-restdocs-core/src/test/resources/custom-snippet-templates/markdown/response-headers-with-title.snippet +++ /dev/null @@ -1,6 +0,0 @@ -{{title}} -Name | Description ----- | ----------- -{{#headers}} -{{name}} | {{description}} -{{/headers}} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-embedded-and-links.json b/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-embedded-and-links.json deleted file mode 100644 index e4ceed00d..000000000 --- a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-embedded-and-links.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "_links": { - "alpha": "https://alpha.example.com", - "bravo": "https://bravo.example.com" - }, - "_embedded": "embedded-test", - "beta": "beta-value", - "charlie": "charlie-value" -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-embedded.json b/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-embedded.json deleted file mode 100644 index 0d4e6b0ac..000000000 --- a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-embedded.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "_embedded": "embedded-test", - "beta": "beta-value", - "charlie": "charlie-value" -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-links.json b/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-links.json deleted file mode 100644 index b4ca7edc3..000000000 --- a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields-and-links.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "_links": { - "alpha": "https://alpha.example.com", - "bravo": "https://bravo.example.com" - }, - "beta": "beta-value", - "charlie": "charlie-value" -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields.json b/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields.json deleted file mode 100644 index a4d2b54ac..000000000 --- a/spring-restdocs-core/src/test/resources/field-payloads/multiple-fields.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "alpha": "alpha-value", - "bravo": 123, - "charlie": { - "one": 456, - "two": "two-value" - }, - "delta": [ - "delta-value-1", - "delta-value-2" - ], - "echo": [{ - "one": 789, - "two": "two-value" - },{ - "one": 987, - "two": "value-two" - }] -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/field-payloads/no-fields.json b/spring-restdocs-core/src/test/resources/field-payloads/no-fields.json deleted file mode 100644 index 6f31cf5a2..000000000 --- a/spring-restdocs-core/src/test/resources/field-payloads/no-fields.json +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/field-payloads/single-field.json b/spring-restdocs-core/src/test/resources/field-payloads/single-field.json deleted file mode 100644 index c209f4435..000000000 --- a/spring-restdocs-core/src/test/resources/field-payloads/single-field.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "alpha": "alpha-value" -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-different-rels.json b/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-different-rels.json deleted file mode 100644 index 97865b189..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-different-rels.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "links": [ { - "rel": "alpha", - "href": "https://alpha.example.com", - "title": "Alpha" - }, { - "rel": "bravo", - "href": "https://bravo.example.com" - } ] -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-same-rels.json b/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-same-rels.json deleted file mode 100644 index 837b19bff..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-same-rels.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "links": [ { - "rel": "alpha", - "href": "https://alpha.example.com/one", - "title": "Alpha one" - }, { - "rel": "alpha", - "href": "https://alpha.example.com/two" - } ] -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/no-links.json b/spring-restdocs-core/src/test/resources/link-payloads/atom/no-links.json deleted file mode 100644 index 6f31cf5a2..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/atom/no-links.json +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/single-link.json b/spring-restdocs-core/src/test/resources/link-payloads/atom/single-link.json deleted file mode 100644 index d49bdbc10..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/atom/single-link.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "links": [ { - "rel": "alpha", - "href": "https://alpha.example.com", - "title": "Alpha" - } ] -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/wrong-format.json b/spring-restdocs-core/src/test/resources/link-payloads/atom/wrong-format.json deleted file mode 100644 index 00c90d2f5..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/atom/wrong-format.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "links": { - "alpha": [{ - "href": "https://alpha.example.com/one" - }, { - "href": "https://alpha.example.com/two" - }] - } -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-different-rels.json b/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-different-rels.json deleted file mode 100644 index 066055d76..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-different-rels.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "_links": { - "alpha": { - "href": "https://alpha.example.com", - "title": "Alpha" - }, - "bravo": { - "href": "https://bravo.example.com" - } - } -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-same-rels.json b/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-same-rels.json deleted file mode 100644 index 4c29ad112..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-same-rels.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "_links": { - "alpha": [{ - "href": "https://alpha.example.com/one", - "title": "Alpha one" - }, { - "href": "https://alpha.example.com/two" - }] - } -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/no-links.json b/spring-restdocs-core/src/test/resources/link-payloads/hal/no-links.json deleted file mode 100644 index 6f31cf5a2..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/hal/no-links.json +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/single-link.json b/spring-restdocs-core/src/test/resources/link-payloads/hal/single-link.json deleted file mode 100644 index 73bdbb9a2..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/hal/single-link.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "_links": { - "alpha": { - "href": "https://alpha.example.com", - "title": "Alpha" - } - } -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/wrong-format.json b/spring-restdocs-core/src/test/resources/link-payloads/hal/wrong-format.json deleted file mode 100644 index a4d79046c..000000000 --- a/spring-restdocs-core/src/test/resources/link-payloads/hal/wrong-format.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "_links": [ { - "rel": "alpha", - "href": "https://alpha.example.com/one" - }, { - "rel": "alpha", - "href": "https://alpha.example.com/two" - } ] -} \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/constraints/TestConstraintDescriptions.properties b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/constraints/TestConstraintDescriptions.properties deleted file mode 100644 index b7150793e..000000000 --- a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/constraints/TestConstraintDescriptions.properties +++ /dev/null @@ -1 +0,0 @@ -jakarta.validation.constraints.NotNull.description=Should not be null \ No newline at end of file diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/multiple-snippets.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/multiple-snippets.snippet deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-custom.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-custom.snippet deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-default.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-default.snippet deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-format-specific-custom.snippet b/spring-restdocs-core/src/test/resources/org/springframework/restdocs/templates/test-format-specific-custom.snippet deleted file mode 100644 index e69de29bb..000000000 diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/SnippetConditions.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/SnippetConditions.java deleted file mode 100644 index ec20b374f..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/SnippetConditions.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2014-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures; - -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import org.assertj.core.api.Condition; -import org.assertj.core.description.Description; - -import org.springframework.http.HttpStatus; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * {@link Condition Conditions} for verify the contents of generated documentation - * snippets. - * - * @author Andy Wilkinson - */ -public final class SnippetConditions { - - private SnippetConditions() { - - } - - public static TableCondition tableWithHeader(TemplateFormat format, String... headers) { - if ("adoc".equals(format.getFileExtension())) { - return new AsciidoctorTableCondition(null, headers); - } - return new MarkdownTableCondition(null, headers); - } - - public static TableCondition tableWithTitleAndHeader(TemplateFormat format, String title, String... headers) { - if ("adoc".equals(format.getFileExtension())) { - return new AsciidoctorTableCondition(title, headers); - } - return new MarkdownTableCondition(title, headers); - } - - public static HttpRequestCondition httpRequest(TemplateFormat format, RequestMethod requestMethod, String uri) { - if ("adoc".equals(format.getFileExtension())) { - return new HttpRequestCondition(requestMethod, uri, new AsciidoctorCodeBlockCondition<>("http", "nowrap"), - 3); - } - return new HttpRequestCondition(requestMethod, uri, new MarkdownCodeBlockCondition<>("http"), 2); - } - - public static HttpResponseCondition httpResponse(TemplateFormat format, HttpStatus status) { - if ("adoc".equals(format.getFileExtension())) { - return new HttpResponseCondition(status, new AsciidoctorCodeBlockCondition<>("http", "nowrap"), 3); - } - return new HttpResponseCondition(status, new MarkdownCodeBlockCondition<>("http"), 2); - } - - public static HttpResponseCondition httpResponse(TemplateFormat format, Integer responseStatusCode, - String responseStatusReason) { - if ("adoc".equals(format.getFileExtension())) { - return new HttpResponseCondition(responseStatusCode, responseStatusReason, - new AsciidoctorCodeBlockCondition<>("http", "nowrap"), 3); - } - return new HttpResponseCondition(responseStatusCode, responseStatusReason, - new MarkdownCodeBlockCondition<>("http"), 2); - } - - @SuppressWarnings("rawtypes") - public static CodeBlockCondition codeBlock(TemplateFormat format, String language) { - if ("adoc".equals(format.getFileExtension())) { - return new AsciidoctorCodeBlockCondition(language, null); - } - return new MarkdownCodeBlockCondition(language); - } - - @SuppressWarnings("rawtypes") - public static CodeBlockCondition codeBlock(TemplateFormat format, String language, String options) { - if ("adoc".equals(format.getFileExtension())) { - return new AsciidoctorCodeBlockCondition(language, options); - } - return new MarkdownCodeBlockCondition(language); - } - - private abstract static class AbstractSnippetContentCondition extends Condition { - - private List lines = new ArrayList<>(); - - protected AbstractSnippetContentCondition() { - as(new Description() { - - @Override - public String value() { - return getLinesAsString(); - } - - }); - } - - protected void addLine(String line) { - this.lines.add(line); - } - - protected void addLine(int index, String line) { - this.lines.add(determineIndex(index), line); - } - - private int determineIndex(int index) { - if (index >= 0) { - return index; - } - return index + this.lines.size(); - } - - @Override - public boolean matches(String content) { - return getLinesAsString().equals(content); - } - - private String getLinesAsString() { - StringWriter writer = new StringWriter(); - Iterator iterator = this.lines.iterator(); - while (iterator.hasNext()) { - writer.append(String.format("%s", iterator.next())); - if (iterator.hasNext()) { - writer.append(String.format("%n")); - } - } - return writer.toString(); - } - - } - - /** - * Base class for code block Conditions. - * - * @param the type of the Condition - */ - public static class CodeBlockCondition> extends AbstractSnippetContentCondition { - - @SuppressWarnings("unchecked") - public T withContent(String content) { - this.addLine(-1, content); - return (T) this; - } - - } - - /** - * A {@link Condition} for an Asciidoctor code block. - * - * @param the type of the Condition - */ - public static class AsciidoctorCodeBlockCondition> - extends CodeBlockCondition { - - protected AsciidoctorCodeBlockCondition(String language, String options) { - this.addLine("[source" + ((language != null) ? "," + language : "") - + ((options != null) ? ",options=\"" + options + "\"" : "") + "]"); - this.addLine("----"); - this.addLine("----"); - } - - } - - /** - * A {@link Condition} for a Markdown code block. - * - * @param the type of the Condition - */ - public static class MarkdownCodeBlockCondition> - extends CodeBlockCondition { - - protected MarkdownCodeBlockCondition(String language) { - this.addLine("```" + ((language != null) ? language : "")); - this.addLine("```"); - } - - } - - /** - * A {@link Condition} for an HTTP request or response. - * - * @param the type of the Condition - */ - public abstract static class HttpCondition> extends Condition { - - private final CodeBlockCondition delegate; - - private int headerOffset; - - protected HttpCondition(CodeBlockCondition delegate, int headerOffset) { - this.delegate = delegate; - this.headerOffset = headerOffset; - } - - @SuppressWarnings("unchecked") - public T header(String name, String value) { - this.delegate.addLine(this.headerOffset++, name + ": " + value); - return (T) this; - } - - @SuppressWarnings("unchecked") - public T header(String name, long value) { - this.delegate.addLine(this.headerOffset++, name + ": " + value); - return (T) this; - } - - @SuppressWarnings("unchecked") - public T content(String content) { - this.delegate.addLine(-1, content); - return (T) this; - } - - @Override - public boolean matches(String item) { - return this.delegate.matches(item); - } - - } - - /** - * A {@link Condition} for an HTTP response. - */ - public static final class HttpResponseCondition extends HttpCondition { - - private HttpResponseCondition(HttpStatus status, CodeBlockCondition delegate, int headerOffset) { - super(delegate, headerOffset); - this.content("HTTP/1.1 " + status.value() + " " + status.getReasonPhrase()); - this.content(""); - } - - private HttpResponseCondition(int responseStatusCode, String responseStatusReason, - CodeBlockCondition delegate, int headerOffset) { - super(delegate, headerOffset); - this.content("HTTP/1.1 " + responseStatusCode + " " + responseStatusReason); - this.content(""); - } - - } - - /** - * A {@link Condition} for an HTTP request. - */ - public static final class HttpRequestCondition extends HttpCondition { - - private HttpRequestCondition(RequestMethod requestMethod, String uri, CodeBlockCondition delegate, - int headerOffset) { - super(delegate, headerOffset); - this.content(requestMethod.name() + " " + uri + " HTTP/1.1"); - this.content(""); - } - - } - - /** - * Base class for table Conditions. - * - * @param the concrete type of the Condition - */ - public abstract static class TableCondition> extends AbstractSnippetContentCondition { - - public abstract T row(String... entries); - - public abstract T configuration(String configuration); - - } - - /** - * A {@link Condition} for an Asciidoctor table. - */ - public static final class AsciidoctorTableCondition extends TableCondition { - - private AsciidoctorTableCondition(String title, String... columns) { - if (StringUtils.hasText(title)) { - this.addLine("." + title); - } - this.addLine("|==="); - String header = "|" + StringUtils.collectionToDelimitedString(Arrays.asList(columns), "|"); - this.addLine(header); - this.addLine(""); - this.addLine("|==="); - } - - @Override - public AsciidoctorTableCondition row(String... entries) { - for (String entry : entries) { - this.addLine(-1, "|" + escapeEntry(entry)); - } - this.addLine(-1, ""); - return this; - } - - private String escapeEntry(String entry) { - if (entry.startsWith("`") && entry.endsWith("`")) { - return "`+" + entry.substring(1, entry.length() - 1) + "+`"; - } - return entry; - } - - @Override - public AsciidoctorTableCondition configuration(String configuration) { - this.addLine(0, configuration); - return this; - } - - } - - /** - * A {@link Condition} for a Markdown table. - */ - public static final class MarkdownTableCondition extends TableCondition { - - private MarkdownTableCondition(String title, String... columns) { - if (StringUtils.hasText(title)) { - this.addLine(title); - this.addLine(""); - } - String header = StringUtils.collectionToDelimitedString(Arrays.asList(columns), " | "); - this.addLine(header); - List components = new ArrayList<>(); - for (String column : columns) { - StringBuilder dashes = new StringBuilder(); - for (int i = 0; i < column.length(); i++) { - dashes.append("-"); - } - components.add(dashes.toString()); - } - this.addLine(StringUtils.collectionToDelimitedString(components, " | ")); - this.addLine(""); - } - - @Override - public MarkdownTableCondition row(String... entries) { - this.addLine(-1, StringUtils.collectionToDelimitedString(Arrays.asList(entries), " | ")); - return this; - } - - @Override - public MarkdownTableCondition configuration(String configuration) { - throw new UnsupportedOperationException("Markdown does not support table configuration"); - } - - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java deleted file mode 100644 index fdab049e1..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/AssertableSnippets.java +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.io.File; -import java.io.IOException; -import java.io.StringWriter; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.function.UnaryOperator; - -import org.assertj.core.api.AbstractStringAssert; -import org.assertj.core.api.AssertProvider; -import org.assertj.core.api.Assertions; - -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.util.StringUtils; - -/** - * AssertJ {@link AssertProvider} for asserting that the generated snippets are correct. - * - * @author Andy Wilkinson - */ -public class AssertableSnippets { - - private final File outputDirectory; - - private final String operationName; - - private final TemplateFormat templateFormat; - - AssertableSnippets(File outputDirectory, String operationName, TemplateFormat templateFormat) { - this.outputDirectory = outputDirectory; - this.operationName = operationName; - this.templateFormat = templateFormat; - } - - public File named(String name) { - return getSnippetFile(name); - } - - private File getSnippetFile(String name) { - File snippetDir = new File(this.outputDirectory, this.operationName); - return new File(snippetDir, name + "." + this.templateFormat.getFileExtension()); - } - - public CodeBlockSnippetAssertProvider curlRequest() { - return new CodeBlockSnippetAssertProvider("curl-request"); - } - - public TableSnippetAssertProvider formParameters() { - return new TableSnippetAssertProvider("form-parameters"); - } - - public CodeBlockSnippetAssertProvider httpieRequest() { - return new CodeBlockSnippetAssertProvider("httpie-request"); - } - - public HttpRequestSnippetAssertProvider httpRequest() { - return new HttpRequestSnippetAssertProvider("http-request"); - } - - public HttpResponseSnippetAssertProvider httpResponse() { - return new HttpResponseSnippetAssertProvider("http-response"); - } - - public TableSnippetAssertProvider links() { - return new TableSnippetAssertProvider("links"); - } - - public TableSnippetAssertProvider pathParameters() { - return new TableSnippetAssertProvider("path-parameters"); - } - - public TableSnippetAssertProvider queryParameters() { - return new TableSnippetAssertProvider("query-parameters"); - } - - public CodeBlockSnippetAssertProvider requestBody() { - return new CodeBlockSnippetAssertProvider("request-body"); - } - - public CodeBlockSnippetAssertProvider requestBody(String suffix) { - return new CodeBlockSnippetAssertProvider("request-body-%s".formatted(suffix)); - } - - public TableSnippetAssertProvider requestCookies() { - return new TableSnippetAssertProvider("request-cookies"); - } - - public TableSnippetAssertProvider requestCookies(String suffix) { - return new TableSnippetAssertProvider("request-cookies-%s".formatted(suffix)); - } - - public TableSnippetAssertProvider requestFields() { - return new TableSnippetAssertProvider("request-fields"); - } - - public TableSnippetAssertProvider requestFields(String suffix) { - return new TableSnippetAssertProvider("request-fields-%s".formatted(suffix)); - } - - public TableSnippetAssertProvider requestHeaders() { - return new TableSnippetAssertProvider("request-headers"); - } - - public TableSnippetAssertProvider requestHeaders(String suffix) { - return new TableSnippetAssertProvider("request-headers-%s".formatted(suffix)); - } - - public CodeBlockSnippetAssertProvider requestPartBody(String partName) { - return new CodeBlockSnippetAssertProvider("request-part-%s-body".formatted(partName)); - } - - public CodeBlockSnippetAssertProvider requestPartBody(String partName, String suffix) { - return new CodeBlockSnippetAssertProvider("request-part-%s-body-%s".formatted(partName, suffix)); - } - - public TableSnippetAssertProvider requestPartFields(String partName) { - return new TableSnippetAssertProvider("request-part-%s-fields".formatted(partName)); - } - - public TableSnippetAssertProvider requestPartFields(String partName, String suffix) { - return new TableSnippetAssertProvider("request-part-%s-fields-%s".formatted(partName, suffix)); - } - - public TableSnippetAssertProvider requestParts() { - return new TableSnippetAssertProvider("request-parts"); - } - - public CodeBlockSnippetAssertProvider responseBody() { - return new CodeBlockSnippetAssertProvider("response-body"); - } - - public CodeBlockSnippetAssertProvider responseBody(String suffix) { - return new CodeBlockSnippetAssertProvider("response-body-%s".formatted(suffix)); - } - - public TableSnippetAssertProvider responseCookies() { - return new TableSnippetAssertProvider("response-cookies"); - } - - public TableSnippetAssertProvider responseFields() { - return new TableSnippetAssertProvider("response-fields"); - } - - public TableSnippetAssertProvider responseFields(String suffix) { - return new TableSnippetAssertProvider("response-fields-%s".formatted(suffix)); - } - - public TableSnippetAssertProvider responseHeaders() { - return new TableSnippetAssertProvider("response-headers"); - } - - public final class TableSnippetAssertProvider implements AssertProvider { - - private final String snippetName; - - private TableSnippetAssertProvider(String snippetName) { - this.snippetName = snippetName; - } - - @Override - public TableSnippetAssert assertThat() { - try { - String content = Files - .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName - + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) - .toPath()); - return new TableSnippetAssert(content); - } - catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - } - - public final class TableSnippetAssert extends AbstractStringAssert { - - private TableSnippetAssert(String actual) { - super(actual, TableSnippetAssert.class); - } - - public void isTable(UnaryOperator> tableOperator) { - Table table = tableOperator - .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) - ? new AsciidoctorTable() : new MarkdownTable()); - table.getLinesAsString(); - Assertions.assertThat(this.actual).isEqualTo(table.getLinesAsString()); - } - - } - - public abstract class Table> extends SnippetContent { - - public abstract T withHeader(String... columns); - - public abstract T withTitleAndHeader(String title, String... columns); - - public abstract T row(String... entries); - - public abstract T configuration(String string); - - } - - private final class AsciidoctorTable extends Table { - - @Override - public AsciidoctorTable withHeader(String... columns) { - return withTitleAndHeader("", columns); - } - - @Override - public AsciidoctorTable withTitleAndHeader(String title, String... columns) { - if (!title.isBlank()) { - this.addLine("." + title); - } - this.addLine("|==="); - String header = "|" + StringUtils.collectionToDelimitedString(Arrays.asList(columns), "|"); - this.addLine(header); - this.addLine(""); - this.addLine("|==="); - return this; - } - - @Override - public AsciidoctorTable row(String... entries) { - for (String entry : entries) { - this.addLine(-1, "|" + escapeEntry(entry)); - } - this.addLine(-1, ""); - return this; - } - - private String escapeEntry(String entry) { - entry = entry.replace("|", "\\|"); - if (entry.startsWith("`") && entry.endsWith("`")) { - return "`+" + entry.substring(1, entry.length() - 1) + "+`"; - } - return entry; - } - - @Override - public AsciidoctorTable configuration(String configuration) { - this.addLine(0, configuration); - return this; - } - - } - - private final class MarkdownTable extends Table { - - @Override - public MarkdownTable withHeader(String... columns) { - return withTitleAndHeader("", columns); - } - - @Override - public MarkdownTable withTitleAndHeader(String title, String... columns) { - if (StringUtils.hasText(title)) { - this.addLine(title); - this.addLine(""); - } - String header = StringUtils.collectionToDelimitedString(Arrays.asList(columns), " | "); - this.addLine(header); - List components = new ArrayList<>(); - for (String column : columns) { - StringBuilder dashes = new StringBuilder(); - for (int i = 0; i < column.length(); i++) { - dashes.append("-"); - } - components.add(dashes.toString()); - } - this.addLine(StringUtils.collectionToDelimitedString(components, " | ")); - this.addLine(""); - return this; - } - - @Override - public MarkdownTable row(String... entries) { - this.addLine(-1, StringUtils.collectionToDelimitedString(Arrays.asList(entries), " | ")); - return this; - } - - @Override - public MarkdownTable configuration(String configuration) { - throw new UnsupportedOperationException("Markdown tables do not support configuration"); - } - - } - - public final class CodeBlockSnippetAssertProvider implements AssertProvider { - - private final String snippetName; - - private CodeBlockSnippetAssertProvider(String snippetName) { - this.snippetName = snippetName; - } - - @Override - public CodeBlockSnippetAssert assertThat() { - try { - String content = Files - .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName - + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) - .toPath()); - return new CodeBlockSnippetAssert(content); - } - catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - } - - public final class CodeBlockSnippetAssert extends AbstractStringAssert { - - private CodeBlockSnippetAssert(String actual) { - super(actual, CodeBlockSnippetAssert.class); - } - - public void isCodeBlock(UnaryOperator> codeBlockOperator) { - CodeBlock codeBlock = codeBlockOperator - .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) - ? new AsciidoctorCodeBlock() : new MarkdownCodeBlock()); - Assertions.assertThat(this.actual).isEqualTo(codeBlock.getLinesAsString()); - } - - } - - public abstract class CodeBlock> extends SnippetContent { - - public abstract T withLanguage(String language); - - public abstract T withOptions(String options); - - public abstract T withLanguageAndOptions(String language, String options); - - public abstract T content(String string); - - } - - private final class AsciidoctorCodeBlock extends CodeBlock { - - @Override - public AsciidoctorCodeBlock withLanguage(String language) { - addLine("[source,%s]".formatted(language)); - return this; - } - - @Override - public AsciidoctorCodeBlock withOptions(String options) { - addLine("[source,options=\"%s\"]".formatted(options)); - return this; - } - - @Override - public AsciidoctorCodeBlock withLanguageAndOptions(String language, String options) { - addLine("[source,%s,options=\"%s\"]".formatted(language, options)); - return this; - } - - @Override - public AsciidoctorCodeBlock content(String content) { - addLine("----"); - addLine(content); - addLine("----"); - return this; - } - - } - - private final class MarkdownCodeBlock extends CodeBlock { - - @Override - public MarkdownCodeBlock withLanguage(String language) { - addLine("```%s".formatted(language)); - return this; - } - - @Override - public MarkdownCodeBlock withOptions(String options) { - addLine("```"); - return this; - } - - @Override - public MarkdownCodeBlock withLanguageAndOptions(String language, String options) { - addLine("```%s".formatted(language)); - return this; - } - - @Override - public MarkdownCodeBlock content(String content) { - addLine(content); - addLine("```"); - return this; - } - - } - - public final class HttpRequestSnippetAssertProvider implements AssertProvider { - - private final String snippetName; - - private HttpRequestSnippetAssertProvider(String snippetName) { - this.snippetName = snippetName; - } - - @Override - public HttpRequestSnippetAssert assertThat() { - try { - String content = Files - .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName - + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) - .toPath()); - return new HttpRequestSnippetAssert(content); - } - catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - } - - public final class HttpRequestSnippetAssert extends AbstractStringAssert { - - private HttpRequestSnippetAssert(String actual) { - super(actual, HttpRequestSnippetAssert.class); - } - - public void isHttpRequest(UnaryOperator> operator) { - HttpRequest codeBlock = operator - .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) - ? new AsciidoctorHttpRequest() : new MarkdownHttpRequest()); - Assertions.assertThat(this.actual).isEqualTo(codeBlock.getLinesAsString()); - } - - } - - public abstract class HttpRequest> extends SnippetContent { - - public T get(String uri) { - return request("GET", uri); - } - - public T post(String uri) { - return request("POST", uri); - } - - public T put(String uri) { - return request("PUT", uri); - } - - public T patch(String uri) { - return request("PATCH", uri); - } - - public T delete(String uri) { - return request("DELETE", uri); - } - - protected abstract T request(String method, String uri); - - public abstract T header(String name, Object value); - - @SuppressWarnings("unchecked") - public T content(String content) { - addLine(-1, content); - return (T) this; - } - - } - - private final class AsciidoctorHttpRequest extends HttpRequest { - - private int headerOffset = 3; - - @Override - protected AsciidoctorHttpRequest request(String method, String uri) { - addLine("[source,http,options=\"nowrap\"]"); - addLine("----"); - addLine("%s %s HTTP/1.1".formatted(method, uri)); - addLine(""); - addLine("----"); - return this; - } - - @Override - public AsciidoctorHttpRequest header(String name, Object value) { - addLine(this.headerOffset++, "%s: %s".formatted(name, value)); - return this; - } - - } - - private final class MarkdownHttpRequest extends HttpRequest { - - private int headerOffset = 2; - - @Override - public MarkdownHttpRequest request(String method, String uri) { - addLine("```http"); - addLine("%s %s HTTP/1.1".formatted(method, uri)); - addLine(""); - addLine("```"); - return this; - } - - @Override - public MarkdownHttpRequest header(String name, Object value) { - addLine(this.headerOffset++, "%s: %s".formatted(name, value)); - return this; - } - - } - - public final class HttpResponseSnippetAssertProvider implements AssertProvider { - - private final String snippetName; - - private HttpResponseSnippetAssertProvider(String snippetName) { - this.snippetName = snippetName; - } - - @Override - public HttpResponseSnippetAssert assertThat() { - try { - String content = Files - .readString(new File(AssertableSnippets.this.outputDirectory, AssertableSnippets.this.operationName - + "/" + this.snippetName + "." + AssertableSnippets.this.templateFormat.getFileExtension()) - .toPath()); - return new HttpResponseSnippetAssert(content); - } - catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - } - - public final class HttpResponseSnippetAssert extends AbstractStringAssert { - - private HttpResponseSnippetAssert(String actual) { - super(actual, HttpResponseSnippetAssert.class); - } - - public void isHttpResponse(UnaryOperator> operator) { - HttpResponse httpResponse = operator - .apply(AssertableSnippets.this.templateFormat.equals(TemplateFormats.asciidoctor()) - ? new AsciidoctorHttpResponse() : new MarkdownHttpResponse()); - Assertions.assertThat(this.actual).isEqualTo(httpResponse.getLinesAsString()); - } - - } - - public abstract class HttpResponse> extends SnippetContent { - - public T ok() { - return status("200 OK"); - } - - public T badRequest() { - return status("400 Bad Request"); - } - - public T status(int status) { - return status("%d ".formatted(status)); - } - - protected abstract T status(String status); - - public abstract T header(String name, Object value); - - @SuppressWarnings("unchecked") - public T content(String content) { - addLine(-1, content); - return (T) this; - } - - } - - private final class AsciidoctorHttpResponse extends HttpResponse { - - private int headerOffset = 3; - - @Override - protected AsciidoctorHttpResponse status(String status) { - addLine("[source,http,options=\"nowrap\"]"); - addLine("----"); - addLine("HTTP/1.1 %s".formatted(status)); - addLine(""); - addLine("----"); - return this; - } - - @Override - public AsciidoctorHttpResponse header(String name, Object value) { - addLine(this.headerOffset++, "%s: %s".formatted(name, value)); - return this; - } - - } - - private final class MarkdownHttpResponse extends HttpResponse { - - private int headerOffset = 2; - - @Override - public MarkdownHttpResponse status(String status) { - addLine("```http"); - addLine("HTTP/1.1 %s".formatted(status)); - addLine(""); - addLine("```"); - return this; - } - - @Override - public MarkdownHttpResponse header(String name, Object value) { - addLine(this.headerOffset++, "%s: %s".formatted(name, value)); - return this; - } - - } - - private static class SnippetContent { - - private List lines = new ArrayList<>(); - - protected void addLine(String line) { - this.lines.add(line); - } - - protected void addLine(int index, String line) { - this.lines.add(determineIndex(index), line); - } - - private int determineIndex(int index) { - if (index >= 0) { - return index; - } - return index + this.lines.size(); - } - - protected String getLinesAsString() { - StringWriter writer = new StringWriter(); - Iterator iterator = this.lines.iterator(); - while (iterator.hasNext()) { - writer.append(String.format("%s", iterator.next())); - if (iterator.hasNext()) { - writer.append(String.format("%n")); - } - } - return writer.toString(); - } - - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/CapturedOutput.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/CapturedOutput.java deleted file mode 100644 index d3aaed82c..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/CapturedOutput.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -/** - * Provides access to {@link System#out System.out} and {@link System#err System.err} - * output that has been captured. - * - * @author Madhura Bhave - * @author Phillip Webb - * @author Andy Wilkinson - */ -public interface CapturedOutput extends CharSequence { - - @Override - default int length() { - return toString().length(); - } - - @Override - default char charAt(int index) { - return toString().charAt(index); - } - - @Override - default CharSequence subSequence(int start, int end) { - return toString().subSequence(start, end); - } - - /** - * Return all content (both {@link System#out System.out} and {@link System#err - * System.err}) in the order that it was captured. - * @return all captured output - */ - String getAll(); - - /** - * Return {@link System#out System.out} content in the order that it was captured. - * @return {@link System#out System.out} captured output - */ - String getOut(); - - /** - * Return {@link System#err System.err} content in the order that it was captured. - * @return {@link System#err System.err} captured output - */ - String getErr(); - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OperationBuilder.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OperationBuilder.java deleted file mode 100644 index 97c9ebcdd..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OperationBuilder.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.restdocs.ManualRestDocumentation; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.mustache.Mustache; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationRequestPartFactory; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.restdocs.operation.ResponseCookie; -import org.springframework.restdocs.operation.StandardOperation; -import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolverFactory; -import org.springframework.restdocs.snippet.StandardWriterResolver; -import org.springframework.restdocs.snippet.WriterResolver; -import org.springframework.restdocs.templates.StandardTemplateResourceResolver; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.mustache.AsciidoctorTableCellContentLambda; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; - -/** - * Basic builder API for creating an {@link Operation}. - * - * @author Andy Wilkinson - */ -public class OperationBuilder { - - private final Map attributes = new HashMap<>(); - - private final File outputDirectory; - - private final String name; - - private final TemplateFormat templateFormat; - - private OperationResponseBuilder responseBuilder; - - private OperationRequestBuilder requestBuilder; - - OperationBuilder(File outputDirectory, String name) { - this(outputDirectory, name, null); - } - - OperationBuilder(File outputDirectory, String name, TemplateFormat templateFormat) { - this.outputDirectory = outputDirectory; - this.name = name; - this.templateFormat = templateFormat; - } - - public OperationRequestBuilder request(String uri) { - this.requestBuilder = new OperationRequestBuilder(uri); - return this.requestBuilder; - } - - public OperationResponseBuilder response() { - this.responseBuilder = new OperationResponseBuilder(); - return this.responseBuilder; - } - - public OperationBuilder attribute(String name, Object value) { - this.attributes.put(name, value); - return this; - } - - public Operation build() { - if (this.attributes.get(TemplateEngine.class.getName()) == null) { - Map templateContext = new HashMap<>(); - templateContext.put("tableCellContent", new AsciidoctorTableCellContentLambda()); - this.attributes.put(TemplateEngine.class.getName(), - new MustacheTemplateEngine(new StandardTemplateResourceResolver(this.templateFormat), - Mustache.compiler().escapeHTML(false), templateContext)); - } - RestDocumentationContext context = createContext(); - this.attributes.put(RestDocumentationContext.class.getName(), context); - this.attributes.put(WriterResolver.class.getName(), new StandardWriterResolver( - new RestDocumentationContextPlaceholderResolverFactory(), "UTF-8", this.templateFormat)); - return new StandardOperation(this.name, - ((this.requestBuilder == null) ? new OperationRequestBuilder("http://localhost/").buildRequest() - : this.requestBuilder.buildRequest()), - (this.responseBuilder == null) ? new OperationResponseBuilder().buildResponse() - : this.responseBuilder.buildResponse(), - this.attributes); - } - - private RestDocumentationContext createContext() { - ManualRestDocumentation manualRestDocumentation = new ManualRestDocumentation( - this.outputDirectory.getAbsolutePath()); - manualRestDocumentation.beforeTest(null, null); - RestDocumentationContext context = manualRestDocumentation.beforeOperation(); - return context; - } - - /** - * Basic builder API for creating an {@link OperationRequest}. - */ - public final class OperationRequestBuilder { - - private URI requestUri = URI.create("http://localhost/"); - - private HttpMethod method = HttpMethod.GET; - - private byte[] content = new byte[0]; - - private HttpHeaders headers = new HttpHeaders(); - - private List partBuilders = new ArrayList<>(); - - private Collection cookies = new ArrayList<>(); - - private OperationRequestBuilder(String uri) { - this.requestUri = URI.create(uri); - } - - private OperationRequest buildRequest() { - List parts = new ArrayList<>(); - for (OperationRequestPartBuilder builder : this.partBuilders) { - parts.add(builder.buildPart()); - } - return new OperationRequestFactory().create(this.requestUri, this.method, this.content, this.headers, parts, - this.cookies); - } - - public Operation build() { - return OperationBuilder.this.build(); - } - - public OperationRequestBuilder method(String method) { - this.method = HttpMethod.valueOf(method); - return this; - } - - public OperationRequestBuilder content(String content) { - this.content = content.getBytes(); - return this; - } - - public OperationRequestBuilder content(byte[] content) { - this.content = content; - return this; - } - - public OperationRequestBuilder header(String name, String value) { - this.headers.add(name, value); - return this; - } - - public OperationRequestPartBuilder part(String name, byte[] content) { - OperationRequestPartBuilder partBuilder = new OperationRequestPartBuilder(name, content); - this.partBuilders.add(partBuilder); - return partBuilder; - } - - public OperationRequestBuilder cookie(String name, String value) { - this.cookies.add(new RequestCookie(name, value)); - return this; - } - - /** - * Basic builder API for creating an {@link OperationRequestPart}. - */ - public final class OperationRequestPartBuilder { - - private final String name; - - private final byte[] content; - - private String submittedFileName; - - private HttpHeaders headers = new HttpHeaders(); - - private OperationRequestPartBuilder(String name, byte[] content) { - this.name = name; - this.content = content; - } - - public OperationRequestPartBuilder submittedFileName(String submittedFileName) { - this.submittedFileName = submittedFileName; - return this; - } - - public OperationRequestBuilder and() { - return OperationRequestBuilder.this; - } - - public Operation build() { - return OperationBuilder.this.build(); - } - - private OperationRequestPart buildPart() { - return new OperationRequestPartFactory().create(this.name, this.submittedFileName, this.content, - this.headers); - } - - public OperationRequestPartBuilder header(String name, String value) { - this.headers.add(name, value); - return this; - } - - } - - } - - /** - * Basic builder API for creating an {@link OperationResponse}. - */ - public final class OperationResponseBuilder { - - private HttpStatusCode status = HttpStatus.OK; - - private HttpHeaders headers = new HttpHeaders(); - - private Set cookies = new HashSet<>(); - - private byte[] content = new byte[0]; - - private OperationResponse buildResponse() { - return new OperationResponseFactory().create(this.status, this.headers, this.content, this.cookies); - } - - public OperationResponseBuilder status(HttpStatusCode status) { - this.status = status; - return this; - } - - public OperationResponseBuilder header(String name, String value) { - this.headers.add(name, value); - return this; - } - - public OperationResponseBuilder cookie(String name, String value) { - this.cookies.add(new ResponseCookie(name, value)); - return this; - } - - public OperationResponseBuilder content(byte[] content) { - this.content = content; - return this; - } - - public OperationResponseBuilder content(String content) { - this.content = content.getBytes(); - return this; - } - - public Operation build() { - return OperationBuilder.this.build(); - } - - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCapture.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCapture.java deleted file mode 100644 index 385dd49cf..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCapture.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Predicate; - -import org.springframework.util.Assert; - -/** - * Provides support for capturing {@link System#out System.out} and {@link System#err - * System.err}. - * - * @author Madhura Bhave - * @author Phillip Webb - * @author Andy Wilkinson - */ -class OutputCapture implements CapturedOutput { - - private final Deque systemCaptures = new ArrayDeque<>(); - - /** - * Push a new system capture session onto the stack. - */ - final void push() { - this.systemCaptures.addLast(new SystemCapture()); - } - - /** - * Pop the last system capture session from the stack. - */ - final void pop() { - this.systemCaptures.removeLast().release(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj instanceof CharSequence) { - return getAll().equals(obj.toString()); - } - return false; - } - - @Override - public int hashCode() { - return toString().hashCode(); - } - - @Override - public String toString() { - return getAll(); - } - - /** - * Return all content (both {@link System#out System.out} and {@link System#err - * System.err}) in the order that it was captured. - * @return all captured output - */ - @Override - public String getAll() { - return get((type) -> true); - } - - /** - * Return {@link System#out System.out} content in the order that it was captured. - * @return {@link System#out System.out} captured output - */ - @Override - public String getOut() { - return get(Type.OUT::equals); - } - - /** - * Return {@link System#err System.err} content in the order that it was captured. - * @return {@link System#err System.err} captured output - */ - @Override - public String getErr() { - return get(Type.ERR::equals); - } - - /** - * Resets the current capture session, clearing its captured output. - */ - void reset() { - this.systemCaptures.peek().reset(); - } - - private String get(Predicate filter) { - Assert.state(!this.systemCaptures.isEmpty(), - "No system captures found. Please check your output capture registration."); - StringBuilder builder = new StringBuilder(); - for (SystemCapture systemCapture : this.systemCaptures) { - systemCapture.append(builder, filter); - } - return builder.toString(); - } - - /** - * A capture session that captures {@link System#out System.out} and {@link System#err - * System.err}. - */ - private static class SystemCapture { - - private final PrintStreamCapture out; - - private final PrintStreamCapture err; - - private final Object monitor = new Object(); - - private final List capturedStrings = new ArrayList<>(); - - SystemCapture() { - this.out = new PrintStreamCapture(System.out, this::captureOut); - this.err = new PrintStreamCapture(System.err, this::captureErr); - System.setOut(this.out); - System.setErr(this.err); - } - - void release() { - System.setOut(this.out.getParent()); - System.setErr(this.err.getParent()); - } - - private void captureOut(String string) { - synchronized (this.monitor) { - this.capturedStrings.add(new CapturedString(Type.OUT, string)); - } - } - - private void captureErr(String string) { - synchronized (this.monitor) { - this.capturedStrings.add(new CapturedString(Type.ERR, string)); - } - } - - void append(StringBuilder builder, Predicate filter) { - synchronized (this.monitor) { - for (CapturedString stringCapture : this.capturedStrings) { - if (filter.test(stringCapture.getType())) { - builder.append(stringCapture); - } - } - } - } - - void reset() { - synchronized (this.monitor) { - this.capturedStrings.clear(); - } - } - - } - - /** - * A {@link PrintStream} implementation that captures written strings. - */ - private static class PrintStreamCapture extends PrintStream { - - private final PrintStream parent; - - PrintStreamCapture(PrintStream parent, Consumer copy) { - super(new OutputStreamCapture(getSystemStream(parent), copy)); - this.parent = parent; - } - - PrintStream getParent() { - return this.parent; - } - - private static PrintStream getSystemStream(PrintStream printStream) { - while (printStream instanceof PrintStreamCapture printStreamCapture) { - printStream = printStreamCapture.getParent(); - } - return printStream; - } - - } - - /** - * An {@link OutputStream} implementation that captures written strings. - */ - private static class OutputStreamCapture extends OutputStream { - - private final PrintStream systemStream; - - private final Consumer copy; - - OutputStreamCapture(PrintStream systemStream, Consumer copy) { - this.systemStream = systemStream; - this.copy = copy; - } - - @Override - public void write(int b) throws IOException { - write(new byte[] { (byte) (b & 0xFF) }); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - this.copy.accept(new String(b, off, len)); - this.systemStream.write(b, off, len); - } - - @Override - public void flush() throws IOException { - this.systemStream.flush(); - } - - } - - /** - * A captured string that forms part of the full output. - */ - private static class CapturedString { - - private final Type type; - - private final String string; - - CapturedString(Type type, String string) { - this.type = type; - this.string = string; - } - - Type getType() { - return this.type; - } - - @Override - public String toString() { - return this.string; - } - - } - - /** - * Types of content that can be captured. - */ - private enum Type { - - OUT, ERR - - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java deleted file mode 100644 index 4dbb2b8ed..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/OutputCaptureExtension.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; - -/** - * JUnit Jupiter {@code @Extension} to capture {@link System#out System.out} and - * {@link System#err System.err}. Can be registered for an entire test class or for an - * individual test method through {@link ExtendWith @ExtendWith}. This extension provides - * {@linkplain ParameterResolver parameter resolution} for a {@link CapturedOutput} - * instance which can be used to assert that the correct output was written. - *

- * To use with {@link ExtendWith @ExtendWith}, inject the {@link CapturedOutput} as an - * argument to your test class constructor, test method, or lifecycle methods: - * - *

- * @ExtendWith(OutputCaptureExtension.class)
- * class MyTest {
- *
- *     @Test
- *     void test(CapturedOutput output) {
- *         System.out.println("ok");
- *         assertThat(output).contains("ok");
- *         System.err.println("error");
- *     }
- *
- *     @AfterEach
- *     void after(CapturedOutput output) {
- *         assertThat(output.getOut()).contains("ok");
- *         assertThat(output.getErr()).contains("error");
- *     }
- *
- * }
- * 
- * - * @author Madhura Bhave - * @author Phillip Webb - * @author Andy Wilkinson - * @author Sam Brannen - * @see CapturedOutput - */ -public class OutputCaptureExtension - implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { - - OutputCaptureExtension() { - } - - @Override - public void beforeAll(ExtensionContext context) throws Exception { - getOutputCapture(context).push(); - } - - @Override - public void afterAll(ExtensionContext context) throws Exception { - getOutputCapture(context).pop(); - } - - @Override - public void beforeEach(ExtensionContext context) throws Exception { - getOutputCapture(context).push(); - } - - @Override - public void afterEach(ExtensionContext context) throws Exception { - getOutputCapture(context).pop(); - } - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - return CapturedOutput.class.equals(parameterContext.getParameter().getType()); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - return getOutputCapture(extensionContext); - } - - private OutputCapture getOutputCapture(ExtensionContext context) { - return getStore(context).getOrComputeIfAbsent(OutputCapture.class); - } - - private Store getStore(ExtensionContext context) { - return context.getStore(Namespace.create(getClass())); - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java deleted file mode 100644 index 15eb8d39c..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.TestTemplate; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; - -/** - * Signals that a method is a template for a test that renders a snippet. The test will be - * executed once for each of the two supported snippet formats (Asciidoctor and Markdown). - *

- * A rendered snippet test method can inject the following types: - *

    - *
  • {@link OperationBuilder}
  • - *
  • {@link AssertableSnippets}
  • - *
- * - * @author Andy Wilkinson - */ -@TestTemplate -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@ExtendWith(RenderedSnippetTestExtension.class) -public @interface RenderedSnippetTest { - - /** - * The snippet formats to render. - * @return the formats - */ - Format[] format() default { Format.ASCIIDOCTOR, Format.MARKDOWN }; - - enum Format { - - /** - * Asciidoctor snippet format. - */ - ASCIIDOCTOR(TemplateFormats.asciidoctor()), - - /** - * Markdown snippet format. - */ - MARKDOWN(TemplateFormats.markdown()); - - private final TemplateFormat templateFormat; - - Format(TemplateFormat templateFormat) { - this.templateFormat = templateFormat; - } - - TemplateFormat templateFormat() { - return this.templateFormat; - } - - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java deleted file mode 100644 index 34bb83ef9..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/RenderedSnippetTestExtension.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.io.File; -import java.util.List; -import java.util.stream.Stream; - -import org.junit.jupiter.api.extension.Extension; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; -import org.junit.jupiter.api.extension.TestTemplateInvocationContext; -import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; -import org.junit.platform.commons.util.AnnotationUtils; - -import org.springframework.core.io.FileSystemResource; -import org.springframework.restdocs.templates.TemplateEngine; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateResourceResolver; -import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine; -import org.springframework.restdocs.testfixtures.jupiter.RenderedSnippetTest.Format; - -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * {@link TestTemplateInvocationContextProvider} for - * {@link RenderedSnippetTest @RenderedSnippetTest} and - * {@link SnippetTemplate @SnippetTemplate}. - * - * @author Andy Wilkinson - */ -class RenderedSnippetTestExtension implements TestTemplateInvocationContextProvider { - - @Override - public boolean supportsTestTemplate(ExtensionContext context) { - return true; - } - - @Override - public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { - return AnnotationUtils.findAnnotation(context.getRequiredTestMethod(), RenderedSnippetTest.class) - .map((renderedSnippetTest) -> Stream.of(renderedSnippetTest.format()) - .map(Format::templateFormat) - .map(SnippetTestInvocationContext::new) - .map(TestTemplateInvocationContext.class::cast)) - .orElseThrow(); - } - - static class SnippetTestInvocationContext implements TestTemplateInvocationContext { - - private final TemplateFormat templateFormat; - - SnippetTestInvocationContext(TemplateFormat templateFormat) { - this.templateFormat = templateFormat; - } - - @Override - public List getAdditionalExtensions() { - return List.of(new RenderedSnippetTestParameterResolver(this.templateFormat)); - } - - @Override - public String getDisplayName(int invocationIndex) { - return this.templateFormat.getId(); - } - - } - - static class RenderedSnippetTestParameterResolver implements ParameterResolver { - - private final TemplateFormat templateFormat; - - RenderedSnippetTestParameterResolver(TemplateFormat templateFormat) { - this.templateFormat = templateFormat; - } - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - Class parameterType = parameterContext.getParameter().getType(); - return AssertableSnippets.class.equals(parameterType) || OperationBuilder.class.equals(parameterType) - || TemplateFormat.class.equals(parameterType); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - Class parameterType = parameterContext.getParameter().getType(); - if (AssertableSnippets.class.equals(parameterType)) { - return getStore(extensionContext).getOrComputeIfAbsent(AssertableSnippets.class, - (key) -> new AssertableSnippets(determineOutputDirectory(extensionContext), - determineOperationName(extensionContext), this.templateFormat)); - } - if (TemplateFormat.class.equals(parameterType)) { - return this.templateFormat; - } - return getStore(extensionContext).getOrComputeIfAbsent(OperationBuilder.class, (key) -> { - OperationBuilder operationBuilder = new OperationBuilder(determineOutputDirectory(extensionContext), - determineOperationName(extensionContext), this.templateFormat); - AnnotationUtils.findAnnotation(extensionContext.getRequiredTestMethod(), SnippetTemplate.class) - .ifPresent((snippetTemplate) -> { - TemplateResourceResolver resolver = mock(TemplateResourceResolver.class); - given(resolver.resolveTemplateResource(snippetTemplate.snippet())) - .willReturn(snippetResource(snippetTemplate.template(), this.templateFormat)); - operationBuilder.attribute(TemplateEngine.class.getName(), - new MustacheTemplateEngine(resolver)); - }); - - return operationBuilder; - }); - } - - private Store getStore(ExtensionContext extensionContext) { - return extensionContext.getStore(Namespace.create(getClass())); - } - - private File determineOutputDirectory(ExtensionContext extensionContext) { - return new File("build/" + extensionContext.getRequiredTestClass().getSimpleName()); - } - - private String determineOperationName(ExtensionContext extensionContext) { - String operationName = extensionContext.getRequiredTestMethod().getName(); - int index = operationName.indexOf('['); - if (index > 0) { - operationName = operationName.substring(0, index); - } - return operationName; - } - - private FileSystemResource snippetResource(String name, TemplateFormat templateFormat) { - return new FileSystemResource( - "src/test/resources/custom-snippet-templates/" + templateFormat.getId() + "/" + name + ".snippet"); - } - - } - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java deleted file mode 100644 index a07e34ce7..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTemplate.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Customizes the template that will be used when rendering a snippet in a - * {@link RenderedSnippetTest rendered snippet test}. - * - * @author Andy Wilkinson - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface SnippetTemplate { - - /** - * The name of the snippet whose template should be customized. - * @return the snippet name - */ - String snippet(); - - /** - * The custom template to use when rendering the snippet. - * @return the custom template - */ - String template(); - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java deleted file mode 100644 index 291f3e6f7..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.restdocs.snippet.Snippet; - -/** - * Signals that a method is a test of a {@link Snippet}. Typically used to test scenarios - * where a failure occurs before the snippet is rendered. To test snippet rendering, use - * {@link RenderedSnippetTest}. - *

- * A snippet test method can inject the following types: - *

    - *
  • {@link OperationBuilder}
  • - *
- * - * @author Andy Wilkinson - */ -@Test -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@ExtendWith(SnippetTestExtension.class) -public @interface SnippetTest { - -} diff --git a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java b/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java deleted file mode 100644 index 1488902fc..000000000 --- a/spring-restdocs-core/src/testFixtures/java/org/springframework/restdocs/testfixtures/jupiter/SnippetTestExtension.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.testfixtures.jupiter; - -import java.io.File; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; - -/** - * {@link ParameterResolver} for {@link SnippetTest @SnippetTest}. - * - * @author Andy Wilkinson - */ -class SnippetTestExtension implements ParameterResolver { - - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) - throws ParameterResolutionException { - Class parameterType = parameterContext.getParameter().getType(); - return OperationBuilder.class.equals(parameterType); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - return getStore(extensionContext).getOrComputeIfAbsent(OperationBuilder.class, - (key) -> new OperationBuilder(determineOutputDirectory(extensionContext), - determineOperationName(extensionContext))); - } - - private Store getStore(ExtensionContext extensionContext) { - return extensionContext.getStore(Namespace.create(getClass())); - } - - private File determineOutputDirectory(ExtensionContext extensionContext) { - return new File("build/" + extensionContext.getRequiredTestClass().getSimpleName()); - } - - private String determineOperationName(ExtensionContext extensionContext) { - String operationName = extensionContext.getRequiredTestMethod().getName(); - int index = operationName.indexOf('['); - if (index > 0) { - operationName = operationName.substring(0, index); - } - return operationName; - } - -} diff --git a/spring-restdocs-mockmvc/build.gradle b/spring-restdocs-mockmvc/build.gradle deleted file mode 100644 index 875d74148..000000000 --- a/spring-restdocs-mockmvc/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -plugins { - id "java-library" - id "maven-publish" - id "optional-dependencies" -} - -description = "Spring REST Docs MockMvc" - -dependencies { - api(project(":spring-restdocs-core")) - api("org.springframework:spring-webmvc") - api("org.springframework:spring-test") - - implementation("jakarta.servlet:jakarta.servlet-api") - - internal(platform(project(":spring-restdocs-platform"))) - - testImplementation(testFixtures(project(":spring-restdocs-core"))) -} - -tasks.named("test") { - useJUnitPlatform() -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/IterableEnumeration.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/IterableEnumeration.java deleted file mode 100644 index ef9ea0e13..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/IterableEnumeration.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.util.Enumeration; -import java.util.Iterator; - -/** - * An adapter to expose an {@link Enumeration} as an {@link Iterable}. - * - * @param the type of the Enumeration's contents - * @author Andy Wilkinson - */ -final class IterableEnumeration implements Iterable { - - private final Enumeration enumeration; - - private IterableEnumeration(Enumeration enumeration) { - this.enumeration = enumeration; - } - - @Override - public Iterator iterator() { - return new Iterator() { - - @Override - public boolean hasNext() { - return IterableEnumeration.this.enumeration.hasMoreElements(); - } - - @Override - public T next() { - return IterableEnumeration.this.enumeration.nextElement(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - } - - /** - * Creates an {@code Iterable} that will iterate over the given {@code enumeration}. - * @param the type of the enumeration's elements - * @param enumeration the enumeration to expose as an {@code Iterable} - * @return the iterable - */ - static Iterable of(Enumeration enumeration) { - return new IterableEnumeration<>(enumeration); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationPreprocessorsConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationPreprocessorsConfigurer.java deleted file mode 100644 index 44afd43bc..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationPreprocessorsConfigurer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import org.springframework.restdocs.config.OperationPreprocessorsConfigurer; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; -import org.springframework.test.web.servlet.setup.MockMvcConfigurer; -import org.springframework.web.context.WebApplicationContext; - -/** - * A configurer that can be used to configure the operation preprocessors. - * - * @author Filip Hrisafov - * @since 2.0.0 - */ -public final class MockMvcOperationPreprocessorsConfigurer extends - OperationPreprocessorsConfigurer - implements MockMvcConfigurer { - - MockMvcOperationPreprocessorsConfigurer(MockMvcRestDocumentationConfigurer parent) { - super(parent); - } - - @Override - public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { - and().afterConfigurerAdded(builder); - } - - @Override - public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, - WebApplicationContext context) { - return and().beforeMockMvcCreated(builder, context); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverter.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverter.java deleted file mode 100644 index 580bc763f..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverter.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.io.IOException; -import java.net.URI; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Scanner; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Part; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockMultipartHttpServletRequest; -import org.springframework.restdocs.operation.ConversionException; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationRequestPartFactory; -import org.springframework.restdocs.operation.RequestConverter; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; -import org.springframework.web.multipart.MultipartFile; - -/** - * A converter for creating an {@link OperationRequest} from a - * {@link MockHttpServletRequest}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -class MockMvcRequestConverter implements RequestConverter { - - @Override - public OperationRequest convert(MockHttpServletRequest mockRequest) { - try { - HttpHeaders headers = extractHeaders(mockRequest); - List parts = extractParts(mockRequest); - Collection cookies = extractCookies(mockRequest, headers); - return new OperationRequestFactory().create(getRequestUri(mockRequest), - HttpMethod.valueOf(mockRequest.getMethod()), getRequestContent(mockRequest, headers), headers, - parts, cookies); - } - catch (Exception ex) { - throw new ConversionException(ex); - } - } - - private URI getRequestUri(MockHttpServletRequest mockRequest) { - String queryString = ""; - if (mockRequest.getQueryString() != null) { - queryString = mockRequest.getQueryString(); - } - else if ("GET".equals(mockRequest.getMethod()) || mockRequest.getContentLengthLong() > 0) { - queryString = urlEncodedParameters(mockRequest); - } - StringBuffer requestUrlBuffer = mockRequest.getRequestURL(); - if (queryString.length() > 0) { - requestUrlBuffer.append("?").append(queryString.toString()); - } - return URI.create(requestUrlBuffer.toString()); - } - - private String urlEncodedParameters(MockHttpServletRequest mockRequest) { - StringBuilder parameters = new StringBuilder(); - MultiValueMap queryParameters = parse(mockRequest.getQueryString()); - for (String name : IterableEnumeration.of(mockRequest.getParameterNames())) { - if (!queryParameters.containsKey(name)) { - String[] values = mockRequest.getParameterValues(name); - if (values.length == 0) { - append(parameters, name); - } - else { - for (String value : values) { - append(parameters, name, value); - } - } - } - } - return parameters.toString(); - } - - private byte[] getRequestContent(MockHttpServletRequest mockRequest, HttpHeaders headers) { - byte[] content = mockRequest.getContentAsByteArray(); - if ("GET".equals(mockRequest.getMethod())) { - return content; - } - MediaType contentType = headers.getContentType(); - if (contentType == null || MediaType.APPLICATION_FORM_URLENCODED.includes(contentType)) { - Map parameters = mockRequest.getParameterMap(); - if (!parameters.isEmpty() && (content == null || content.length == 0)) { - StringBuilder contentBuilder = new StringBuilder(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap queryParameters = parse(mockRequest.getQueryString()); - mockRequest.getParameterMap().forEach((name, values) -> { - List queryParameterValues = queryParameters.get(name); - if (values.length == 0) { - if (queryParameterValues == null) { - append(contentBuilder, name); - } - } - else { - for (String value : values) { - if (queryParameterValues == null || !queryParameterValues.contains(value)) { - append(contentBuilder, name, value); - } - } - } - }); - return contentBuilder.toString().getBytes(StandardCharsets.UTF_8); - } - } - return content; - } - - private Collection extractCookies(MockHttpServletRequest mockRequest, HttpHeaders headers) { - if (mockRequest.getCookies() == null || mockRequest.getCookies().length == 0) { - return Collections.emptyList(); - } - List cookies = new ArrayList<>(); - for (jakarta.servlet.http.Cookie servletCookie : mockRequest.getCookies()) { - cookies.add(new RequestCookie(servletCookie.getName(), servletCookie.getValue())); - } - headers.remove(HttpHeaders.COOKIE); - return cookies; - } - - private List extractParts(MockHttpServletRequest servletRequest) - throws IOException, ServletException { - List parts = new ArrayList<>(); - parts.addAll(extractServletRequestParts(servletRequest)); - if (servletRequest instanceof MockMultipartHttpServletRequest) { - parts.addAll(extractMultipartRequestParts((MockMultipartHttpServletRequest) servletRequest)); - } - return parts; - } - - private List extractServletRequestParts(MockHttpServletRequest servletRequest) - throws IOException, ServletException { - List parts = new ArrayList<>(); - for (Part part : servletRequest.getParts()) { - parts.add(createOperationRequestPart(part)); - } - return parts; - } - - private OperationRequestPart createOperationRequestPart(Part part) throws IOException { - HttpHeaders partHeaders = extractHeaders(part); - List contentTypeHeader = partHeaders.get(HttpHeaders.CONTENT_TYPE); - if (part.getContentType() != null && contentTypeHeader == null) { - partHeaders.setContentType(MediaType.parseMediaType(part.getContentType())); - } - return new OperationRequestPartFactory().create(part.getName(), - StringUtils.hasText(part.getSubmittedFileName()) ? part.getSubmittedFileName() : null, - FileCopyUtils.copyToByteArray(part.getInputStream()), partHeaders); - } - - private List extractMultipartRequestParts(MockMultipartHttpServletRequest multipartRequest) - throws IOException { - List parts = new ArrayList<>(); - for (Entry> entry : multipartRequest.getMultiFileMap().entrySet()) { - for (MultipartFile file : entry.getValue()) { - parts.add(createOperationRequestPart(file)); - } - } - return parts; - } - - private OperationRequestPart createOperationRequestPart(MultipartFile file) throws IOException { - HttpHeaders partHeaders = new HttpHeaders(); - if (StringUtils.hasText(file.getContentType())) { - partHeaders.setContentType(MediaType.parseMediaType(file.getContentType())); - } - return new OperationRequestPartFactory().create(file.getName(), - StringUtils.hasText(file.getOriginalFilename()) ? file.getOriginalFilename() : null, file.getBytes(), - partHeaders); - } - - private HttpHeaders extractHeaders(Part part) { - HttpHeaders partHeaders = new HttpHeaders(); - for (String headerName : part.getHeaderNames()) { - for (String value : part.getHeaders(headerName)) { - partHeaders.add(headerName, value); - } - } - return partHeaders; - } - - private HttpHeaders extractHeaders(MockHttpServletRequest servletRequest) { - HttpHeaders headers = new HttpHeaders(); - for (String headerName : IterableEnumeration.of(servletRequest.getHeaderNames())) { - for (String value : IterableEnumeration.of(servletRequest.getHeaders(headerName))) { - headers.add(headerName, value); - } - } - return headers; - } - - private static void append(StringBuilder sb, String key) { - append(sb, key, ""); - } - - private static void append(StringBuilder sb, String key, String value) { - doAppend(sb, urlEncode(key) + "=" + urlEncode(value)); - } - - private static void doAppend(StringBuilder sb, String toAppend) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(toAppend); - } - - private static String urlEncode(String s) { - if (!StringUtils.hasLength(s)) { - return ""; - } - return URLEncoder.encode(s, StandardCharsets.UTF_8); - } - - private static MultiValueMap parse(String query) { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - if (!StringUtils.hasLength(query)) { - return parameters; - } - try (Scanner scanner = new Scanner(query)) { - scanner.useDelimiter("&"); - while (scanner.hasNext()) { - processParameter(scanner.next(), parameters); - } - } - return parameters; - } - - private static void processParameter(String parameter, MultiValueMap parameters) { - String[] components = parameter.split("="); - if (components.length > 0 && components.length < 3) { - if (components.length == 2) { - String name = components[0]; - String value = components[1]; - parameters.add(decode(name), decode(value)); - } - else { - List values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<>()); - values.add(""); - } - } - else { - throw new IllegalArgumentException("The parameter '" + parameter + "' is malformed"); - } - } - - private static String decode(String encoded) { - return URLDecoder.decode(encoded, StandardCharsets.US_ASCII); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java deleted file mode 100644 index 701d86998..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverter.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import jakarta.servlet.http.Cookie; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatusCode; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.ResponseConverter; -import org.springframework.restdocs.operation.ResponseCookie; -import org.springframework.util.StringUtils; - -/** - * A converter for creating an {@link OperationResponse} derived from a - * {@link MockHttpServletResponse}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -class MockMvcResponseConverter implements ResponseConverter { - - @Override - public OperationResponse convert(MockHttpServletResponse mockResponse) { - HttpHeaders headers = extractHeaders(mockResponse); - Collection cookies = extractCookies(mockResponse); - return new OperationResponseFactory().create(HttpStatusCode.valueOf(mockResponse.getStatus()), headers, - mockResponse.getContentAsByteArray(), cookies); - } - - private Collection extractCookies(MockHttpServletResponse mockResponse) { - if (mockResponse.getCookies() == null || mockResponse.getCookies().length == 0) { - return Collections.emptyList(); - } - List cookies = new ArrayList<>(); - for (Cookie cookie : mockResponse.getCookies()) { - cookies.add(new ResponseCookie(cookie.getName(), cookie.getValue())); - } - return cookies; - } - - private HttpHeaders extractHeaders(MockHttpServletResponse response) { - HttpHeaders headers = new HttpHeaders(); - for (String headerName : response.getHeaderNames()) { - for (String value : response.getHeaders(headerName)) { - headers.add(headerName, value); - } - } - - if (response.getCookies() != null && !headers.containsHeader(HttpHeaders.SET_COOKIE)) { - for (Cookie cookie : response.getCookies()) { - headers.add(HttpHeaders.SET_COOKIE, generateSetCookieHeader(cookie)); - } - } - - return headers; - } - - private String generateSetCookieHeader(Cookie cookie) { - StringBuilder header = new StringBuilder(); - - header.append(cookie.getName()); - header.append('='); - - appendIfAvailable(header, cookie.getValue()); - - int maxAge = cookie.getMaxAge(); - if (maxAge > -1) { - header.append(";Max-Age="); - header.append(maxAge); - } - - appendIfAvailable(header, "; Domain=", cookie.getDomain()); - appendIfAvailable(header, "; Path=", cookie.getPath()); - - if (cookie.getSecure()) { - header.append("; Secure"); - } - - if (cookie.isHttpOnly()) { - header.append("; HttpOnly"); - } - - return header.toString(); - } - - private void appendIfAvailable(StringBuilder header, String value) { - if (StringUtils.hasText(value)) { - header.append(""); - header.append(value); - } - } - - private void appendIfAvailable(StringBuilder header, String name, String value) { - if (StringUtils.hasText(value)) { - header.append(name); - header.append(value); - } - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java deleted file mode 100644 index 3ff48f524..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentation.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; -import org.springframework.test.web.servlet.setup.MockMvcConfigurer; - -/** - * Static factory methods for documenting RESTful APIs using Spring MVC Test. - * - * @author Andy Wilkinson - */ -public abstract class MockMvcRestDocumentation { - - private static final MockMvcRequestConverter REQUEST_CONVERTER = new MockMvcRequestConverter(); - - private static final MockMvcResponseConverter RESPONSE_CONVERTER = new MockMvcResponseConverter(); - - private MockMvcRestDocumentation() { - - } - - /** - * Provides access to a {@link MockMvcConfigurer} that can be used to configure a - * {@link MockMvc} instance using the given {@code contextProvider}. - * @param contextProvider the context provider - * @return the configurer - * @see ConfigurableMockMvcBuilder#apply(MockMvcConfigurer) - */ - public static MockMvcRestDocumentationConfigurer documentationConfiguration( - RestDocumentationContextProvider contextProvider) { - return new MockMvcRestDocumentationConfigurer(contextProvider); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. - * @param identifier an identifier for the API call that is being documented - * @param snippets the snippets - * @return a Mock MVC {@code ResultHandler} that will produce the documentation - * @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) - * @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) - */ - public static RestDocumentationResultHandler document(String identifier, Snippet... snippets) { - return new RestDocumentationResultHandler( - new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, snippets)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code requestPreprocessor} is applied to the request before it is documented. - * @param identifier an identifier for the API call that is being documented - * @param requestPreprocessor the request preprocessor - * @param snippets the snippets - * @return a Mock MVC {@code ResultHandler} that will produce the documentation - * @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) - * @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) - */ - public static RestDocumentationResultHandler document(String identifier, - OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) { - return new RestDocumentationResultHandler(new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, - RESPONSE_CONVERTER, requestPreprocessor, snippets)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code responsePreprocessor} is applied to the request before it is documented. - * @param identifier an identifier for the API call that is being documented - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - * @return a Mock MVC {@code ResultHandler} that will produce the documentation - * @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) - * @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) - */ - public static RestDocumentationResultHandler document(String identifier, - OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - return new RestDocumentationResultHandler(new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, - RESPONSE_CONVERTER, responsePreprocessor, snippets)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code requestPreprocessor} and {@code responsePreprocessor} are applied to the - * request and response respectively before they are documented. - * @param identifier an identifier for the API call that is being documented - * @param requestPreprocessor the request preprocessor - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - * @return a Mock MVC {@code ResultHandler} that will produce the documentation - * @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) - * @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) - */ - public static RestDocumentationResultHandler document(String identifier, - OperationRequestPreprocessor requestPreprocessor, OperationResponsePreprocessor responsePreprocessor, - Snippet... snippets) { - return new RestDocumentationResultHandler(new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, - RESPONSE_CONVERTER, requestPreprocessor, responsePreprocessor, snippets)); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java deleted file mode 100644 index 9c495ebb3..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurer.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2014-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.config.RestDocumentationConfigurer; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; -import org.springframework.test.web.servlet.setup.MockMvcConfigurer; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.context.WebApplicationContext; - -/** - * A MockMvc-specific {@link RestDocumentationConfigurer}. - * - * @author Andy Wilkinson - * @author Filip Hrisafov - * @since 1.1.0 - */ -public final class MockMvcRestDocumentationConfigurer extends - RestDocumentationConfigurer - implements MockMvcConfigurer { - - private final MockMvcSnippetConfigurer snippetConfigurer = new MockMvcSnippetConfigurer(this); - - private final UriConfigurer uriConfigurer = new UriConfigurer(this); - - private final MockMvcOperationPreprocessorsConfigurer operationPreprocessorsConfigurer = new MockMvcOperationPreprocessorsConfigurer( - this); - - private final RestDocumentationContextProvider contextManager; - - MockMvcRestDocumentationConfigurer(RestDocumentationContextProvider contextManager) { - this.contextManager = contextManager; - } - - /** - * Returns a {@link UriConfigurer} that can be used to configure the request URIs that - * will be documented. - * @return the URI configurer - */ - public UriConfigurer uris() { - return this.uriConfigurer; - } - - @Override - public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, - WebApplicationContext context) { - return new ConfigurerApplyingRequestPostProcessor(this.contextManager); - } - - @Override - public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { - // Nothing to do - } - - @Override - public MockMvcSnippetConfigurer snippets() { - return this.snippetConfigurer; - } - - @Override - public MockMvcOperationPreprocessorsConfigurer operationPreprocessors() { - return this.operationPreprocessorsConfigurer; - } - - private final class ConfigurerApplyingRequestPostProcessor implements RequestPostProcessor { - - private static final Function urlTemplateExtractor; - - static { - Function fromRequestAttribute = ( - request) -> (String) request.getAttribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE); - Function extractor; - try { - Method accessorMethod = MockHttpServletRequest.class.getMethod("getUriTemplate"); - extractor = (request) -> { - String urlTemplate = fromRequestAttribute.apply(request); - return (urlTemplate != null) ? urlTemplate - : (String) ReflectionUtils.invokeMethod(accessorMethod, request); - }; - } - catch (Exception ex) { - extractor = fromRequestAttribute; - } - urlTemplateExtractor = extractor; - } - - private final RestDocumentationContextProvider contextManager; - - private ConfigurerApplyingRequestPostProcessor(RestDocumentationContextProvider contextManager) { - this.contextManager = contextManager; - } - - @Override - public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - RestDocumentationContext context = this.contextManager.beforeOperation(); - Map configuration = new HashMap<>(); - configuration.put(MockHttpServletRequest.class.getName(), request); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, - urlTemplateExtractor.apply(request)); - configuration.put(RestDocumentationContext.class.getName(), context); - request.setAttribute(RestDocumentationResultHandler.ATTRIBUTE_NAME_CONFIGURATION, configuration); - MockMvcRestDocumentationConfigurer.this.apply(configuration, context); - MockMvcRestDocumentationConfigurer.this.uriConfigurer.apply(configuration, context); - return request; - } - - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcSnippetConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcSnippetConfigurer.java deleted file mode 100644 index ef6bb5908..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcSnippetConfigurer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import org.springframework.restdocs.config.SnippetConfigurer; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; -import org.springframework.test.web.servlet.setup.MockMvcConfigurer; -import org.springframework.web.context.WebApplicationContext; - -/** - * A configurer that can be used to configure the generated documentation snippets. - * - * @author Andy Wilkinson - * @since 1.1.0 - */ -public final class MockMvcSnippetConfigurer extends - SnippetConfigurer implements MockMvcConfigurer { - - MockMvcSnippetConfigurer(MockMvcRestDocumentationConfigurer parent) { - super(parent); - } - - @Override - public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { - and().afterConfigurerAdded(builder); - } - - @Override - public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, - WebApplicationContext context) { - return and().beforeMockMvcCreated(builder, context); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuilders.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuilders.java deleted file mode 100644 index 30f302041..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuilders.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.net.URI; - -import org.springframework.http.HttpMethod; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.request.RequestDocumentation; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; - -/** - * A drop-in replacement for {@link MockMvcRequestBuilders} that captures a request's URL - * template and makes it available for documentation. Required when - * {@link RequestDocumentation#pathParameters(org.springframework.restdocs.request.ParameterDescriptor...) - * ) documenting path parameters} and recommended for general usage. - * - * @author Andy Wilkinson - * @see MockMvcRequestBuilders - * @see RequestDocumentation#pathParameters(org.springframework.restdocs.request.ParameterDescriptor...) - * @see RequestDocumentation#pathParameters(java.util.Map, - * org.springframework.restdocs.request.ParameterDescriptor...) - */ -public abstract class RestDocumentationRequestBuilders { - - private RestDocumentationRequestBuilders() { - - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a GET request. The url template - * will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the GET request - */ - public static MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.get(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a GET request. - * @param uri the URL - * @return the builder for the GET request - */ - public static MockHttpServletRequestBuilder get(URI uri) { - return MockMvcRequestBuilders.get(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a POST request. The url template - * will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the POST request - */ - public static MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.post(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a POST request. - * @param uri the URL - * @return the builder for the POST request - */ - public static MockHttpServletRequestBuilder post(URI uri) { - return MockMvcRequestBuilders.post(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a PUT request. The url template - * will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the PUT request - */ - public static MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.put(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a PUT request. - * @param uri the URL - * @return the builder for the PUT request - */ - public static MockHttpServletRequestBuilder put(URI uri) { - return MockMvcRequestBuilders.put(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a PATCH request. The url - * template will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the PATCH request - */ - public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.patch(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a PATCH request. - * @param uri the URL - * @return the builder for the PATCH request - */ - public static MockHttpServletRequestBuilder patch(URI uri) { - return MockMvcRequestBuilders.patch(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a DELETE request. The url - * template will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the DELETE request - */ - public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.delete(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a DELETE request. - * @param uri the URL - * @return the builder for the DELETE request - */ - public static MockHttpServletRequestBuilder delete(URI uri) { - return MockMvcRequestBuilders.delete(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. The url - * template will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the OPTIONS request - */ - public static MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.options(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. - * @param uri the URL - * @return the builder for the OPTIONS request - */ - public static MockHttpServletRequestBuilder options(URI uri) { - return MockMvcRequestBuilders.options(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a HEAD request. The url template - * will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the HEAD request - */ - public static MockHttpServletRequestBuilder head(String urlTemplate, Object... urlVariables) { - return MockMvcRequestBuilders.head(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a HEAD request. - * @param uri the URL - * @return the builder for the HEAD request - */ - public static MockHttpServletRequestBuilder head(URI uri) { - return MockMvcRequestBuilders.head(uri); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP - * method. The url template will be captured and made available for documentation. - * @param httpMethod the HTTP method - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the request - */ - public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, - Object... urlVariables) { - return MockMvcRequestBuilders.request(httpMethod, urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP - * method. - * @param httpMethod the HTTP method (GET, POST, etc) - * @param uri the URL - * @return the builder for the request - */ - public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, URI uri) { - return MockMvcRequestBuilders.request(httpMethod, uri); - } - - /** - * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request. - * The URL template will be captured and made available for documentation. - * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables - * @return the builder for the multipart request - * @since 2.0.6 - */ - public static MockMultipartHttpServletRequestBuilder multipart(String urlTemplate, Object... urlVariables) { - return (MockMultipartHttpServletRequestBuilder) MockMvcRequestBuilders.multipart(urlTemplate, urlVariables) - .requestAttr(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate); - } - - /** - * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request. - * @param uri the URL - * @return the builder for the multipart request - * @since 2.0.6 - */ - public static MockMultipartHttpServletRequestBuilder multipart(URI uri) { - return MockMvcRequestBuilders.multipart(uri); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java deleted file mode 100644 index 33b2f3d85..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.ResultHandler; -import org.springframework.util.Assert; - -/** - * A Spring MVC Test {@code ResultHandler} for documenting RESTful APIs. - * - * @author Andy Wilkinson - * @author Andreas Evers - * @see MockMvcRestDocumentation#document(String, Snippet...) - */ -public class RestDocumentationResultHandler implements ResultHandler { - - static final String ATTRIBUTE_NAME_CONFIGURATION = "org.springframework.restdocs.configuration"; - - private final RestDocumentationGenerator delegate; - - RestDocumentationResultHandler( - RestDocumentationGenerator delegate) { - Assert.notNull(delegate, "delegate must be non-null"); - this.delegate = delegate; - } - - @Override - public void handle(MvcResult result) { - this.delegate.handle(result.getRequest(), result.getResponse(), retrieveConfiguration(result)); - } - - /** - * Creates a new {@link RestDocumentationResultHandler} to be passed into - * {@link ResultActions#andDo(ResultHandler)} that will produce documentation using - * the given {@code snippets}. For example: - * - *
-	 * this.mockMvc.perform(MockMvcRequestBuilders.get("/search"))
-	 *     .andExpect(status().isOk())
-	 *     .andDo(this.documentationHandler.document(responseFields(
-	 *          fieldWithPath("page").description("The requested Page")
-	 *     ));
-	 * 
- * @param snippets the snippets - * @return the new result handler - */ - public RestDocumentationResultHandler document(Snippet... snippets) { - return new RestDocumentationResultHandler(this.delegate.withSnippets(snippets)) { - - @Override - public void handle(MvcResult result) { - Map configuration = new HashMap<>(retrieveConfiguration(result)); - configuration.remove(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - getDelegate().handle(result.getRequest(), result.getResponse(), configuration); - } - - }; - } - - /** - * Returns the {@link RestDocumentationGenerator} that is used as a delegate. - * @return the delegate - */ - protected final RestDocumentationGenerator getDelegate() { - return this.delegate; - } - - private Map retrieveConfiguration(MvcResult result) { - @SuppressWarnings("unchecked") - Map configuration = (Map) result.getRequest() - .getAttribute(ATTRIBUTE_NAME_CONFIGURATION); - Assert.state(configuration != null, () -> "REST Docs configuration not found. Did you forget to apply a " - + MockMvcRestDocumentationConfigurer.class.getSimpleName() + " when building the MockMvc instance?"); - return configuration; - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java deleted file mode 100644 index 1e53d1dd5..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/UriConfigurer.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.util.Map; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.config.AbstractNestedConfigurer; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; -import org.springframework.test.web.servlet.setup.MockMvcConfigurer; -import org.springframework.web.context.WebApplicationContext; - -/** - * A configurer that can be used to configure the documented URIs. - * - * @author Andy Wilkinson - */ -public class UriConfigurer extends AbstractNestedConfigurer - implements MockMvcConfigurer { - - /** - * The default scheme for documented URIs. - * - * @see #withScheme(String) - */ - public static final String DEFAULT_SCHEME = "http"; - - /** - * The defalt host for documented URIs. - * - * @see #withHost(String) - */ - public static final String DEFAULT_HOST = "localhost"; - - /** - * The default port for documented URIs. - * - * @see #withPort(int) - */ - public static final int DEFAULT_PORT = 8080; - - private String scheme = DEFAULT_SCHEME; - - private String host = DEFAULT_HOST; - - private int port = DEFAULT_PORT; - - UriConfigurer(MockMvcRestDocumentationConfigurer parent) { - super(parent); - } - - /** - * Configures any documented URIs to use the given {@code scheme}. The default is - * {@code http}. - * @param scheme the URI scheme - * @return {@code this} - */ - public UriConfigurer withScheme(String scheme) { - this.scheme = scheme; - return this; - } - - /** - * Configures any documented URIs to use the given {@code host}. The default is - * {@code localhost}. - * @param host the URI host - * @return {@code this} - */ - public UriConfigurer withHost(String host) { - this.host = host; - return this; - } - - /** - * Configures any documented URIs to use the given {@code port}. The default is - * {@code 8080}. - * @param port the URI port - * @return {@code this} - */ - public UriConfigurer withPort(int port) { - this.port = port; - return this; - } - - @Override - public void apply(Map configuration, RestDocumentationContext context) { - MockHttpServletRequest request = (MockHttpServletRequest) configuration - .get(MockHttpServletRequest.class.getName()); - request.setScheme(this.scheme); - request.setServerPort(this.port); - request.setServerName(this.host); - } - - @Override - public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { - and().afterConfigurerAdded(builder); - } - - @Override - public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, - WebApplicationContext context) { - return and().beforeMockMvcCreated(builder, context); - } - -} diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/package-info.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/package-info.java deleted file mode 100644 index 5956dd0e4..000000000 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Core classes for using Spring REST Docs with Spring Test's MockMvc. - */ -package org.springframework.restdocs.mockmvc; diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java deleted file mode 100644 index c0d3c1fc3..000000000 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRequestConverterTests.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.URI; -import java.util.Arrays; -import java.util.Iterator; - -import jakarta.servlet.http.Part; -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.mock.web.MockServletContext; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link MockMvcRequestConverter}. - * - * @author Andy Wilkinson - */ -class MockMvcRequestConverterTests { - - private final MockMvcRequestConverter factory = new MockMvcRequestConverter(); - - @Test - void httpRequest() { - OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get("/foo")); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void httpRequestWithCustomPort() { - MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); - mockRequest.setServerPort(8080); - OperationRequest request = this.factory.convert(mockRequest); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost:8080/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void requestWithContextPath() { - OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get("/foo/bar").contextPath("/foo")); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo/bar")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void requestWithHeaders() { - OperationRequest request = createOperationRequest( - MockMvcRequestBuilders.get("/foo").header("a", "alpha", "apple").header("b", "bravo")); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - assertThat(request.getHeaders().headerSet()).contains(entry("a", Arrays.asList("alpha", "apple")), - entry("b", Arrays.asList("bravo"))); - } - - @Test - void requestWithCookies() { - OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get("/foo") - .cookie(new jakarta.servlet.http.Cookie("cookieName1", "cookieVal1"), - new jakarta.servlet.http.Cookie("cookieName2", "cookieVal2"))); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - assertThat(request.getCookies().size()).isEqualTo(2); - - Iterator cookieIterator = request.getCookies().iterator(); - - RequestCookie cookie1 = cookieIterator.next(); - assertThat(cookie1.getName()).isEqualTo("cookieName1"); - assertThat(cookie1.getValue()).isEqualTo("cookieVal1"); - - RequestCookie cookie2 = cookieIterator.next(); - assertThat(cookie2.getName()).isEqualTo("cookieName2"); - assertThat(cookie2.getValue()).isEqualTo("cookieVal2"); - } - - @Test - void httpsRequest() { - MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); - mockRequest.setScheme("https"); - mockRequest.setServerPort(443); - OperationRequest request = this.factory.convert(mockRequest); - assertThat(request.getUri()).isEqualTo(URI.create("https://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void httpsRequestWithCustomPort() { - MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); - mockRequest.setScheme("https"); - mockRequest.setServerPort(8443); - OperationRequest request = this.factory.convert(mockRequest); - assertThat(request.getUri()).isEqualTo(URI.create("https://localhost:8443/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void getRequestWithParametersProducesUriWithQueryString() { - OperationRequest request = createOperationRequest( - MockMvcRequestBuilders.get("/foo").param("a", "alpha", "apple").param("b", "br&vo")); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&a=apple&b=br%26vo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void getRequestWithQueryString() { - MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/foo?a=alpha&b=bravo"); - OperationRequest request = createOperationRequest(builder); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&b=bravo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void postRequestWithParametersCreatesFormUrlEncodedContent() { - OperationRequest request = createOperationRequest( - MockMvcRequestBuilders.post("/foo").param("a", "alpha", "apple").param("b", "br&vo")); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getContentAsString()).isEqualTo("a=alpha&a=apple&b=br%26vo"); - assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED); - } - - @Test - void postRequestWithParametersAndQueryStringCreatesFormUrlEncodedContentWithoutDuplication() { - OperationRequest request = createOperationRequest( - MockMvcRequestBuilders.post("/foo?a=alpha").param("a", "apple").param("b", "br&vo")); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getContentAsString()).isEqualTo("a=apple&b=br%26vo"); - assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED); - } - - @Test - void mockMultipartFileUpload() { - OperationRequest request = createOperationRequest(MockMvcRequestBuilders.multipart("/foo") - .file(new MockMultipartFile("file", new byte[] { 1, 2, 3, 4 }))); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getParts().size()).isEqualTo(1); - OperationRequestPart part = request.getParts().iterator().next(); - assertThat(part.getName()).isEqualTo("file"); - assertThat(part.getSubmittedFileName()).isNull(); - assertThat(part.getHeaders().size()).isEqualTo(1); - assertThat(part.getHeaders().getContentLength()).isEqualTo(4L); - assertThat(part.getContent()).isEqualTo(new byte[] { 1, 2, 3, 4 }); - } - - @Test - void mockMultipartFileUploadWithContentType() { - OperationRequest request = createOperationRequest(MockMvcRequestBuilders.multipart("/foo") - .file(new MockMultipartFile("file", "original", "image/png", new byte[] { 1, 2, 3, 4 }))); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getParts().size()).isEqualTo(1); - OperationRequestPart part = request.getParts().iterator().next(); - assertThat(part.getName()).isEqualTo("file"); - assertThat(part.getSubmittedFileName()).isEqualTo("original"); - assertThat(part.getHeaders().getContentType()).isEqualTo(MediaType.IMAGE_PNG); - assertThat(part.getContent()).isEqualTo(new byte[] { 1, 2, 3, 4 }); - } - - @Test - void requestWithPart() throws IOException { - MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); - Part mockPart = mock(Part.class); - given(mockPart.getHeaderNames()).willReturn(Arrays.asList("a", "b")); - given(mockPart.getHeaders("a")).willReturn(Arrays.asList("alpha")); - given(mockPart.getHeaders("b")).willReturn(Arrays.asList("bravo", "banana")); - given(mockPart.getInputStream()).willReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 })); - given(mockPart.getName()).willReturn("part-name"); - given(mockPart.getSubmittedFileName()).willReturn("submitted.txt"); - mockRequest.addPart(mockPart); - OperationRequest request = this.factory.convert(mockRequest); - assertThat(request.getParts().size()).isEqualTo(1); - OperationRequestPart part = request.getParts().iterator().next(); - assertThat(part.getName()).isEqualTo("part-name"); - assertThat(part.getSubmittedFileName()).isEqualTo("submitted.txt"); - assertThat(part.getHeaders().getContentType()).isNull(); - assertThat(part.getHeaders().get("a")).containsExactly("alpha"); - assertThat(part.getHeaders().get("b")).containsExactly("bravo", "banana"); - assertThat(part.getContent()).isEqualTo(new byte[] { 1, 2, 3, 4 }); - } - - @Test - void requestWithPartWithContentType() throws IOException { - MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo").buildRequest(new MockServletContext()); - Part mockPart = mock(Part.class); - given(mockPart.getHeaderNames()).willReturn(Arrays.asList("a", "b")); - given(mockPart.getHeaders("a")).willReturn(Arrays.asList("alpha")); - given(mockPart.getHeaders("b")).willReturn(Arrays.asList("bravo", "banana")); - given(mockPart.getInputStream()).willReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 })); - given(mockPart.getName()).willReturn("part-name"); - given(mockPart.getSubmittedFileName()).willReturn("submitted.png"); - given(mockPart.getContentType()).willReturn("image/png"); - mockRequest.addPart(mockPart); - OperationRequest request = this.factory.convert(mockRequest); - assertThat(request.getParts().size()).isEqualTo(1); - OperationRequestPart part = request.getParts().iterator().next(); - assertThat(part.getName()).isEqualTo("part-name"); - assertThat(part.getSubmittedFileName()).isEqualTo("submitted.png"); - assertThat(part.getHeaders().getContentType()).isEqualTo(MediaType.IMAGE_PNG); - assertThat(part.getHeaders().get("a")).containsExactly("alpha"); - assertThat(part.getHeaders().get("b")).containsExactly("bravo", "banana"); - assertThat(part.getContent()).isEqualTo(new byte[] { 1, 2, 3, 4 }); - } - - private OperationRequest createOperationRequest(MockHttpServletRequestBuilder builder) { - return this.factory.convert(builder.buildRequest(new MockServletContext())); - } - -} diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java deleted file mode 100644 index e13bb03be..000000000 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcResponseConverterTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.util.Collections; - -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.ResponseCookie; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - -/** - * Tests for {@link MockMvcResponseConverter}. - * - * @author Tomasz Kopczynski - */ -class MockMvcResponseConverterTests { - - private final MockMvcResponseConverter factory = new MockMvcResponseConverter(); - - @Test - void basicResponse() { - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setStatus(HttpServletResponse.SC_OK); - OperationResponse operationResponse = this.factory.convert(response); - assertThat(operationResponse.getStatus()).isEqualTo(HttpStatus.OK); - } - - @Test - void responseWithCookie() { - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setStatus(HttpServletResponse.SC_OK); - Cookie cookie = new Cookie("name", "value"); - cookie.setDomain("localhost"); - cookie.setHttpOnly(true); - response.addCookie(cookie); - OperationResponse operationResponse = this.factory.convert(response); - assertThat(operationResponse.getHeaders().headerSet()).containsOnly( - entry(HttpHeaders.SET_COOKIE, Collections.singletonList("name=value; Domain=localhost; HttpOnly"))); - assertThat(operationResponse.getCookies()).hasSize(1); - assertThat(operationResponse.getCookies()).first().extracting(ResponseCookie::getName).isEqualTo("name"); - assertThat(operationResponse.getCookies()).first().extracting(ResponseCookie::getValue).isEqualTo("value"); - } - - @Test - void responseWithCustomStatus() { - MockHttpServletResponse response = new MockHttpServletResponse(); - response.setStatus(600); - OperationResponse operationResponse = this.factory.convert(response); - assertThat(operationResponse.getStatus()).isEqualTo(HttpStatusCode.valueOf(600)); - } - -} diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java deleted file mode 100644 index dfb2debc9..000000000 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationConfigurerTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.lang.reflect.Method; -import java.util.Map; - -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MockMvcRestDocumentationConfigurer}. - * - * @author Andy Wilkinson - * @author Dmitriy Mayboroda - */ -@ExtendWith(RestDocumentationExtension.class) -class MockMvcRestDocumentationConfigurerTests { - - private MockHttpServletRequest request = new MockHttpServletRequest(); - - @Test - void defaultConfiguration(RestDocumentationContextProvider restDocumentation) { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation) - .beforeMockMvcCreated(null, null); - postProcessor.postProcessRequest(this.request); - assertUriConfiguration("http", "localhost", 8080); - } - - @Test - void customScheme(RestDocumentationContextProvider restDocumentation) { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() - .withScheme("https") - .beforeMockMvcCreated(null, null); - postProcessor.postProcessRequest(this.request); - assertUriConfiguration("https", "localhost", 8080); - } - - @Test - void customHost(RestDocumentationContextProvider restDocumentation) { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() - .withHost("api.example.com") - .beforeMockMvcCreated(null, null); - postProcessor.postProcessRequest(this.request); - assertUriConfiguration("http", "api.example.com", 8080); - } - - @Test - void customPort(RestDocumentationContextProvider restDocumentation) { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() - .withPort(8081) - .beforeMockMvcCreated(null, null); - postProcessor.postProcessRequest(this.request); - assertUriConfiguration("http", "localhost", 8081); - } - - @Test - void noContentLengthHeaderWhenRequestHasNotContent(RestDocumentationContextProvider restDocumentation) { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation).uris() - .withPort(8081) - .beforeMockMvcCreated(null, null); - postProcessor.postProcessRequest(this.request); - assertThat(this.request.getHeader("Content-Length")).isNull(); - } - - @Test - @SuppressWarnings("unchecked") - void uriTemplateFromRequestAttribute(RestDocumentationContextProvider restDocumentation) { - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation) - .beforeMockMvcCreated(null, null); - this.request.setAttribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{a}/{b}"); - postProcessor.postProcessRequest(this.request); - Map configuration = (Map) this.request - .getAttribute(RestDocumentationResultHandler.ATTRIBUTE_NAME_CONFIGURATION); - assertThat(configuration).containsEntry(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{a}/{b}"); - } - - @Test - @SuppressWarnings("unchecked") - void uriTemplateFromRequest(RestDocumentationContextProvider restDocumentation) { - Method setUriTemplate = ReflectionUtils.findMethod(MockHttpServletRequest.class, "setUriTemplate", - String.class); - Assumptions.assumeFalse(setUriTemplate == null); - RequestPostProcessor postProcessor = new MockMvcRestDocumentationConfigurer(restDocumentation) - .beforeMockMvcCreated(null, null); - ReflectionUtils.invokeMethod(setUriTemplate, this.request, "{a}/{b}"); - postProcessor.postProcessRequest(this.request); - Map configuration = (Map) this.request - .getAttribute(RestDocumentationResultHandler.ATTRIBUTE_NAME_CONFIGURATION); - assertThat(configuration).containsEntry(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, "{a}/{b}"); - } - - private void assertUriConfiguration(String scheme, String host, int port) { - assertThat(scheme).isEqualTo(this.request.getScheme()); - assertThat(host).isEqualTo(this.request.getServerName()); - assertThat(port).isEqualTo(this.request.getServerPort()); - RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(this.request)); - try { - UriComponents uriComponents = ServletUriComponentsBuilder.fromCurrentServletMapping().build(); - assertThat(scheme).isEqualTo(uriComponents.getScheme()); - assertThat(host).isEqualTo(uriComponents.getHost()); - assertThat(port).isEqualTo(uriComponents.getPort()); - } - finally { - RequestContextHolder.resetRequestAttributes(); - } - } - -} diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java deleted file mode 100644 index 49460f1e3..000000000 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java +++ /dev/null @@ -1,706 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; -import org.assertj.core.api.Condition; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationIntegrationTests.TestConfiguration; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.SnippetConditions; -import org.springframework.restdocs.testfixtures.SnippetConditions.CodeBlockCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpRequestCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.FileSystemUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.fail; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.maskLinks; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.restdocs.request.RequestDocumentation.requestParts; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for using Spring REST Docs with Spring Test's MockMvc. - * - * @author Andy Wilkinson - * @author Dewet Diener - * @author Tomasz Kopczynski - * @author Filip Hrisafov - */ -@SpringJUnitConfig -@WebAppConfiguration -@ExtendWith(RestDocumentationExtension.class) -@ContextConfiguration(classes = TestConfiguration.class) -public class MockMvcRestDocumentationIntegrationTests { - - private RestDocumentationContextProvider restDocumentation; - - @Autowired - private WebApplicationContext context; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.restDocumentation = restDocumentation; - FileSystemUtils.deleteRecursively(new File("build/generated-snippets")); - } - - @AfterEach - void clearOutputDirSystemProperty() { - System.clearProperty("org.springframework.restdocs.outputDir"); - } - - @Test - void basicSnippetGeneration() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation).snippets().withEncoding("UTF-8")) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("basic")); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/basic"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - } - - @Test - void getRequestWithBody() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation).snippets().withEncoding("UTF-8")) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON).content("some body content")) - .andExpect(status().isOk()) - .andDo(document("get-request-with-body")); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/get-request-with-body"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - } - - @Test - void markdownSnippetGeneration() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation).snippets() - .withEncoding("UTF-8") - .withTemplateFormat(TemplateFormats.markdown())) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("basic-markdown")); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/basic-markdown"), "http-request.md", - "http-response.md", "curl-request.md"); - } - - @Test - void curlSnippetWithContent() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(post("/").accept(MediaType.APPLICATION_JSON).content("content")) - .andExpect(status().isOk()) - .andDo(document("curl-snippet-with-content")); - assertThat(new File("build/generated-snippets/curl-snippet-with-content/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl 'http://localhost:8080/' -i -X POST \\%n" - + " -H 'Accept: application/json' \\%n" + " -d 'content'")))); - } - - @Test - void curlSnippetWithCookies() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON).cookie(new Cookie("cookieName", "cookieVal"))) - .andExpect(status().isOk()) - .andDo(document("curl-snippet-with-cookies")); - assertThat(new File("build/generated-snippets/curl-snippet-with-cookies/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl 'http://localhost:8080/' -i -X GET \\%n" - + " -H 'Accept: application/json' \\%n" + " --cookie 'cookieName=cookieVal'")))); - } - - @Test - void curlSnippetWithQueryStringOnPost() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(post("/?foo=bar").param("a", "alpha").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("curl-snippet-with-query-string")); - assertThat(new File("build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl " + "'http://localhost:8080/?foo=bar' -i -X POST \\%n" - + " -H 'Accept: application/json' \\%n" + " -d 'a=alpha'")))); - } - - @Test - void curlSnippetWithEmptyParameterQueryString() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").param("a", "").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("curl-snippet-with-empty-parameter-query-string")); - assertThat( - new File("build/generated-snippets/curl-snippet-with-empty-parameter-query-string/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String - .format("$ curl 'http://localhost:8080/?a=' -i -X GET \\%n" + " -H 'Accept: application/json'")))); - } - - @Test - void curlSnippetWithContentAndParametersOnPost() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(post("/").param("a", "alpha").accept(MediaType.APPLICATION_JSON).content("some content")) - .andExpect(status().isOk()) - .andDo(document("curl-snippet-with-content-and-parameters")); - assertThat(new File("build/generated-snippets/curl-snippet-with-content-and-parameters/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl 'http://localhost:8080/?a=alpha' -i -X POST \\%n" - + " -H 'Accept: application/json' \\%n" + " -d 'some content'")))); - } - - @Test - void httpieSnippetWithContent() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(post("/").accept(MediaType.APPLICATION_JSON).content("content")) - .andExpect(status().isOk()) - .andDo(document("httpie-snippet-with-content")); - assertThat(new File("build/generated-snippets/httpie-snippet-with-content/httpie-request.adoc")).has( - content(codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String.format("$ echo 'content' | " - + "http POST 'http://localhost:8080/' \\%n" + " 'Accept:application/json'")))); - } - - @Test - void httpieSnippetWithCookies() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON).cookie(new Cookie("cookieName", "cookieVal"))) - .andExpect(status().isOk()) - .andDo(document("httpie-snippet-with-cookies")); - assertThat(new File("build/generated-snippets/httpie-snippet-with-cookies/httpie-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ http GET 'http://localhost:8080/' \\%n" - + " 'Accept:application/json' \\%n" + " 'Cookie:cookieName=cookieVal'")))); - } - - @Test - void httpieSnippetWithQueryStringOnPost() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(post("/?foo=bar").param("a", "alpha").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("httpie-snippet-with-query-string")); - assertThat(new File("build/generated-snippets/httpie-snippet-with-query-string/httpie-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ http --form POST 'http://localhost:8080/?foo=bar' \\%n" - + " 'Accept:application/json' \\%n 'a=alpha'")))); - } - - @Test - void httpieSnippetWithContentAndParametersOnPost() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(post("/").param("a", "alpha").content("some content").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("httpie-snippet-post-with-content-and-parameters")); - assertThat(new File( - "build/generated-snippets/httpie-snippet-post-with-content-and-parameters/httpie-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ echo " + "'some content' | http POST " - + "'http://localhost:8080/?a=alpha' \\%n" + " 'Accept:application/json'")))); - } - - @Test - void linksSnippet() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("links", links(linkWithRel("rel").description("The description")))); - - assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "links.adoc"); - } - - @Test - void pathParametersSnippet() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/{foo}", "").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("links", pathParameters(parameterWithName("foo").description("The description")))); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "path-parameters.adoc"); - } - - @Test - void queryParametersSnippet() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").param("foo", "bar").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("links", queryParameters(parameterWithName("foo").description("The description")))); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "query-parameters.adoc"); - } - - @Test - void requestFieldsSnippet() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").param("foo", "bar").content("{\"a\":\"alpha\"}").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("links", requestFields(fieldWithPath("a").description("The description")))); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "request-fields.adoc"); - } - - @Test - void requestPartsSnippet() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(multipart("/upload").file("foo", "bar".getBytes())) - .andExpect(status().isOk()) - .andDo(document("request-parts", requestParts(partWithName("foo").description("The description")))); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/request-parts"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "request-parts.adoc"); - } - - @Test - void responseFieldsSnippet() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").param("foo", "bar").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("links", responseFields(fieldWithPath("a").description("The description"), - subsectionWithPath("links").description("Links to other resources")))); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "response-fields.adoc"); - } - - @Test - void responseWithSetCookie() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/set-cookie")) - .andExpect(status().isOk()) - .andDo(document("set-cookie", - responseHeaders(headerWithName(HttpHeaders.SET_COOKIE).description("set-cookie")))); - assertThat(new File("build/generated-snippets/set-cookie/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK).header(HttpHeaders.SET_COOKIE, - "name=value; Domain=localhost; HttpOnly"))); - } - - @Test - void parameterizedOutputDirectory() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("{method-name}")); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/parameterized-output-directory"), - "http-request.adoc", "http-response.adoc", "curl-request.adoc"); - } - - @Test - void multiStep() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .alwaysDo(document("{method-name}-{step}")) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-1/"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-2/"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-3/"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - } - - @Test - void alwaysDoWithAdditionalSnippets() throws Exception { - RestDocumentationResultHandler documentation = document("{method-name}-{step}"); - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .alwaysDo(documentation) - .build(); - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(documentation.document(responseHeaders(headerWithName("a").description("one")))); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/always-do-with-additional-snippets-1/"), - "http-request.adoc", "http-response.adoc", "curl-request.adoc", "response-headers.adoc"); - } - - @Test - void preprocessedRequest() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - Pattern pattern = Pattern.compile("(\"alpha\")"); - MvcResult result = mockMvc - .perform(get("/").header("a", "alpha") - .header("b", "bravo") - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON) - .content("{\"a\":\"alpha\"}")) - .andExpect(status().isOk()) - .andDo(document("original-request")) - .andDo(document("preprocessed-request", - preprocessRequest(prettyPrint(), - modifyHeaders().remove("a").remove(HttpHeaders.HOST).remove(HttpHeaders.CONTENT_LENGTH), - replacePattern(pattern, "\"<>\"")))) - .andReturn(); - HttpRequestCondition originalRequest = httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/"); - Set mvcResultHeaderNames = new HashSet<>(); - for (String headerName : IterableEnumeration.of(result.getRequest().getHeaderNames())) { - originalRequest.header(headerName, result.getRequest().getHeader(headerName)); - mvcResultHeaderNames.add(headerName); - } - originalRequest.header("Host", "localhost:8080"); - if (!mvcResultHeaderNames.contains("Content-Length")) { - originalRequest.header("Content-Length", "13"); - } - assertThat(new File("build/generated-snippets/original-request/http-request.adoc")) - .has(content(originalRequest.content("{\"a\":\"alpha\"}"))); - HttpRequestCondition preprocessedRequest = httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/"); - List removedHeaders = Arrays.asList("a", HttpHeaders.HOST, HttpHeaders.CONTENT_LENGTH); - for (String headerName : IterableEnumeration.of(result.getRequest().getHeaderNames())) { - if (!removedHeaders.contains(headerName)) { - preprocessedRequest.header(headerName, result.getRequest().getHeader(headerName)); - } - } - String prettyPrinted = String.format("{%n \"a\" : \"<>\"%n}"); - assertThat(new File("build/generated-snippets/preprocessed-request/http-request.adoc")) - .has(content(preprocessedRequest.content(prettyPrinted))); - } - - @Test - void defaultPreprocessedRequest() throws Exception { - Pattern pattern = Pattern.compile("(\"alpha\")"); - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).operationPreprocessors() - .withRequestDefaults(prettyPrint(), - modifyHeaders().remove("a").remove(HttpHeaders.HOST).remove(HttpHeaders.CONTENT_LENGTH), - replacePattern(pattern, "\"<>\""))) - .build(); - - MvcResult result = mockMvc - .perform(get("/").header("a", "alpha") - .header("b", "bravo") - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON) - .content("{\"a\":\"alpha\"}")) - .andDo(document("default-preprocessed-request")) - .andReturn(); - - HttpRequestCondition preprocessedRequest = httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/"); - List removedHeaders = Arrays.asList("a", HttpHeaders.HOST, HttpHeaders.CONTENT_LENGTH); - for (String headerName : IterableEnumeration.of(result.getRequest().getHeaderNames())) { - if (!removedHeaders.contains(headerName)) { - preprocessedRequest.header(headerName, result.getRequest().getHeader(headerName)); - } - } - String prettyPrinted = String.format("{%n \"a\" : \"<>\"%n}"); - assertThat(new File("build/generated-snippets/default-preprocessed-request/http-request.adoc")) - .has(content(preprocessedRequest.content(prettyPrinted))); - } - - @Test - void preprocessedResponse() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - - Pattern pattern = Pattern.compile("(\"alpha\")"); - - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("original-response")) - .andDo(document("preprocessed-response", preprocessResponse(prettyPrint(), maskLinks(), - modifyHeaders().remove("a"), replacePattern(pattern, "\"<>\"")))); - - String original = "{\"a\":\"alpha\",\"links\":[{\"rel\":\"rel\"," + "\"href\":\"href\"}]}"; - assertThat(new File("build/generated-snippets/original-response/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK).header("a", "alpha") - .header("Content-Type", "application/json;charset=UTF-8") - .header(HttpHeaders.CONTENT_LENGTH, original.getBytes().length) - .content(original))); - String prettyPrinted = String.format("{%n \"a\" : \"<>\",%n \"links\" : " - + "[ {%n \"rel\" : \"rel\",%n \"href\" : \"...\"%n } ]%n}"); - assertThat(new File("build/generated-snippets/preprocessed-response/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK) - .header("Content-Type", "application/json;charset=UTF-8") - .header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length) - .content(prettyPrinted))); - } - - @Test - void defaultPreprocessedResponse() throws Exception { - Pattern pattern = Pattern.compile("(\"alpha\")"); - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation).operationPreprocessors() - .withResponseDefaults(prettyPrint(), maskLinks(), modifyHeaders().remove("a"), - replacePattern(pattern, "\"<>\""))) - .build(); - - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("default-preprocessed-response")); - - String prettyPrinted = String.format("{%n \"a\" : \"<>\",%n \"links\" : " - + "[ {%n \"rel\" : \"rel\",%n \"href\" : \"...\"%n } ]%n}"); - assertThat(new File("build/generated-snippets/default-preprocessed-response/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK) - .header("Content-Type", "application/json;charset=UTF-8") - .header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length) - .content(prettyPrinted))); - } - - @Test - void customSnippetTemplate() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - ClassLoader classLoader = new URLClassLoader( - new URL[] { new File("src/test/resources/custom-snippet-templates").toURI().toURL() }, - getClass().getClassLoader()); - ClassLoader previous = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - try { - mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("custom-snippet-template")); - } - finally { - Thread.currentThread().setContextClassLoader(previous); - } - assertThat(new File("build/generated-snippets/custom-snippet-template/curl-request.adoc")) - .hasContent("Custom curl request"); - } - - @Test - void customContextPath() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - - mockMvc.perform(get("/custom/").contextPath("/custom").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("custom-context-path")); - assertThat(new File("build/generated-snippets/custom-context-path/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String.format( - "$ curl 'http://localhost:8080/custom/' -i -X GET \\%n" + " -H 'Accept: application/json'")))); - } - - @Test - void exceptionShouldBeThrownWhenCallDocumentMockMvcNotConfigured() { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); - assertThatThrownBy(() -> mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andDo(document("basic"))) - .isInstanceOf(IllegalStateException.class) - .hasMessage("REST Docs configuration not found. Did you " - + "forget to apply a MockMvcRestDocumentationConfigurer when building the MockMvc instance?"); - - } - - @Test - void exceptionShouldBeThrownWhenCallDocumentSnippetsMockMvcNotConfigured() { - RestDocumentationResultHandler documentation = document("{method-name}-{step}"); - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); - assertThatThrownBy(() -> mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) - .andDo(documentation.document(responseHeaders(headerWithName("a").description("one"))))) - .isInstanceOf(IllegalStateException.class) - .hasMessage("REST Docs configuration not found. Did you forget to apply a " - + "MockMvcRestDocumentationConfigurer when building the MockMvc instance?"); - } - - @Test - void multiPart() throws Exception { - MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(documentationConfiguration(this.restDocumentation)) - .build(); - mockMvc.perform(multipart("/upload").file("test", "content".getBytes())) - .andExpect(status().isOk()) - .andDo(document("upload", requestParts(partWithName("test").description("Foo")))); - } - - private void assertExpectedSnippetFilesExist(File directory, String... snippets) { - for (String snippet : snippets) { - assertThat(new File(directory, snippet)).isFile(); - } - } - - private Condition content(final Condition delegate) { - return new Condition<>() { - - @Override - public boolean matches(File value) { - try { - return delegate.matches(FileCopyUtils - .copyToString(new InputStreamReader(new FileInputStream(value), StandardCharsets.UTF_8))); - } - catch (IOException ex) { - fail("Failed to read '" + value + "'", ex); - return false; - } - } - - }; - } - - private CodeBlockCondition codeBlock(TemplateFormat format, String language) { - return SnippetConditions.codeBlock(format, language); - } - - private HttpRequestCondition httpRequest(TemplateFormat format, RequestMethod requestMethod, String uri) { - return SnippetConditions.httpRequest(format, requestMethod, uri); - } - - private HttpResponseCondition httpResponse(TemplateFormat format, HttpStatus status) { - return SnippetConditions.httpResponse(format, status); - } - - /** - * Test configuration that enables Spring MVC. - */ - @EnableWebMvc - @Configuration(proxyBeanMethods = false) - static final class TestConfiguration { - - @Bean - TestController testController() { - return new TestController(); - } - - } - - @RestController - private static final class TestController { - - @RequestMapping(value = "/", produces = "application/json;charset=UTF-8") - ResponseEntity> foo() { - Map response = new HashMap<>(); - response.put("a", "alpha"); - Map link = new HashMap<>(); - link.put("rel", "rel"); - link.put("href", "href"); - response.put("links", Arrays.asList(link)); - HttpHeaders headers = new HttpHeaders(); - headers.add("a", "alpha"); - return new ResponseEntity<>(response, headers, HttpStatus.OK); - } - - @RequestMapping(value = "/company/5", produces = MediaType.APPLICATION_JSON_VALUE) - String bar() { - return "{\"companyName\": \"FooBar\",\"employee\": [{\"name\": \"Lorem\",\"age\": \"42\"},{\"name\": \"Ipsum\",\"age\": \"24\"}]}"; - } - - @RequestMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - void upload() { - - } - - @RequestMapping("/set-cookie") - void setCookie(HttpServletResponse response) { - Cookie cookie = new Cookie("name", "value"); - cookie.setDomain("localhost"); - cookie.setHttpOnly(true); - - response.addCookie(cookie); - } - - } - -} diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java deleted file mode 100644 index f689778a6..000000000 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/RestDocumentationRequestBuildersTests.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.mockmvc; - -import java.net.URI; - -import jakarta.servlet.ServletContext; -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpMethod; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockServletContext; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.head; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.multipart; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.options; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.request; - -/** - * Tests for {@link RestDocumentationRequestBuilders}. - * - * @author Andy Wilkinson - * - */ -class RestDocumentationRequestBuildersTests { - - private final ServletContext servletContext = new MockServletContext(); - - @Test - void getTemplate() { - assertTemplate(get("/{template}", "t"), HttpMethod.GET); - } - - @Test - void getUri() { - assertUri(get(URI.create("/uri")), HttpMethod.GET); - } - - @Test - void postTemplate() { - assertTemplate(post("/{template}", "t"), HttpMethod.POST); - } - - @Test - void postUri() { - assertUri(post(URI.create("/uri")), HttpMethod.POST); - } - - @Test - void putTemplate() { - assertTemplate(put("/{template}", "t"), HttpMethod.PUT); - } - - @Test - void putUri() { - assertUri(put(URI.create("/uri")), HttpMethod.PUT); - } - - @Test - void patchTemplate() { - assertTemplate(patch("/{template}", "t"), HttpMethod.PATCH); - } - - @Test - void patchUri() { - assertUri(patch(URI.create("/uri")), HttpMethod.PATCH); - } - - @Test - void deleteTemplate() { - assertTemplate(delete("/{template}", "t"), HttpMethod.DELETE); - } - - @Test - void deleteUri() { - assertUri(delete(URI.create("/uri")), HttpMethod.DELETE); - } - - @Test - void optionsTemplate() { - assertTemplate(options("/{template}", "t"), HttpMethod.OPTIONS); - } - - @Test - void optionsUri() { - assertUri(options(URI.create("/uri")), HttpMethod.OPTIONS); - } - - @Test - void headTemplate() { - assertTemplate(head("/{template}", "t"), HttpMethod.HEAD); - } - - @Test - void headUri() { - assertUri(head(URI.create("/uri")), HttpMethod.HEAD); - } - - @Test - void requestTemplate() { - assertTemplate(request(HttpMethod.GET, "/{template}", "t"), HttpMethod.GET); - } - - @Test - void requestUri() { - assertUri(request(HttpMethod.GET, URI.create("/uri")), HttpMethod.GET); - } - - @Test - void multipartTemplate() { - assertTemplate(multipart("/{template}", "t"), HttpMethod.POST); - } - - @Test - void multipartUri() { - assertUri(multipart(URI.create("/uri")), HttpMethod.POST); - } - - private void assertTemplate(MockHttpServletRequestBuilder builder, HttpMethod httpMethod) { - MockHttpServletRequest request = builder.buildRequest(this.servletContext); - assertThat((String) request.getAttribute(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE)) - .isEqualTo("/{template}"); - assertThat(request.getRequestURI()).isEqualTo("/t"); - assertThat(request.getMethod()).isEqualTo(httpMethod.name()); - } - - private void assertUri(MockHttpServletRequestBuilder builder, HttpMethod httpMethod) { - MockHttpServletRequest request = builder.buildRequest(this.servletContext); - assertThat(request.getRequestURI()).isEqualTo("/uri"); - assertThat(request.getMethod()).isEqualTo(httpMethod.name()); - } - -} diff --git a/spring-restdocs-mockmvc/src/test/resources/custom-snippet-templates/org/springframework/restdocs/templates/curl-request.snippet b/spring-restdocs-mockmvc/src/test/resources/custom-snippet-templates/org/springframework/restdocs/templates/curl-request.snippet deleted file mode 100644 index 07f3a48ff..000000000 --- a/spring-restdocs-mockmvc/src/test/resources/custom-snippet-templates/org/springframework/restdocs/templates/curl-request.snippet +++ /dev/null @@ -1 +0,0 @@ -Custom curl request \ No newline at end of file diff --git a/spring-restdocs-mockmvc/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet b/spring-restdocs-mockmvc/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet deleted file mode 100644 index e1db090ba..000000000 --- a/spring-restdocs-mockmvc/src/test/resources/org/springframework/restdocs/templates/request-parts.snippet +++ /dev/null @@ -1,9 +0,0 @@ -|=== -|Request part|Description - -{{#requestParts}} -|`{{name}}` -|{{description}} - -{{/requestParts}} -|=== \ No newline at end of file diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle deleted file mode 100644 index 5e0404db0..000000000 --- a/spring-restdocs-platform/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id 'java-platform' -} - -javaPlatform { - allowDependencies() -} - -dependencies { - constraints { - api("com.samskivert:jmustache:$jmustacheVersion") - api("jakarta.servlet:jakarta.servlet-api:6.1.0") - api("jakarta.validation:jakarta.validation-api:3.1.0") - api("org.apache.pdfbox:pdfbox:2.0.27") - api("org.apache.tomcat.embed:tomcat-embed-core:11.0.2") - api("org.apache.tomcat.embed:tomcat-embed-el:11.0.2") - api("org.apiguardian:apiguardian-api:1.1.2") - api("org.asciidoctor:asciidoctorj:3.0.0") - api("org.asciidoctor:asciidoctorj-pdf:2.3.19") - api("org.assertj:assertj-core:3.23.1") - api("org.hamcrest:hamcrest-core:1.3") - api("org.hamcrest:hamcrest-library:1.3") - api("org.hibernate.validator:hibernate-validator:9.0.0.CR1") - api("org.javamoney:moneta:1.4.2") - } - api(enforcedPlatform("com.fasterxml.jackson:jackson-bom:2.14.0")) - api(enforcedPlatform("io.rest-assured:rest-assured-bom:5.2.1")) - api(enforcedPlatform("org.mockito:mockito-bom:4.9.0")) - api(enforcedPlatform("org.junit:junit-bom:5.13.0")) - api(enforcedPlatform("org.springframework:spring-framework-bom:$springFrameworkVersion")) -} diff --git a/spring-restdocs-restassured/build.gradle b/spring-restdocs-restassured/build.gradle deleted file mode 100644 index 42a05a522..000000000 --- a/spring-restdocs-restassured/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id "io.spring.compatibility-test" version "0.0.4" - id "java-library" - id "maven-publish" -} - -description = "Spring REST Docs REST Assured" - -dependencies { - api(project(":spring-restdocs-core")) - api("io.rest-assured:rest-assured") - implementation("org.springframework:spring-web") - - internal(platform(project(":spring-restdocs-platform"))) - - testCompileOnly("org.apiguardian:apiguardian-api") - testImplementation(testFixtures(project(":spring-restdocs-core"))) - testImplementation("com.fasterxml.jackson.core:jackson-databind") - testImplementation("org.apache.tomcat.embed:tomcat-embed-core") -} - -tasks.named("test") { - useJUnitPlatform(); -} - -compatibilityTest { - dependency("REST Assured") { restAssured -> - restAssured.groupId = "io.rest-assured" - restAssured.versions = ["5.3.+", "5.4.+", "5.5.+"] - } -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationPreprocessorsConfigurer.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationPreprocessorsConfigurer.java deleted file mode 100644 index cef1e925e..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredOperationPreprocessorsConfigurer.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import io.restassured.filter.Filter; -import io.restassured.filter.FilterContext; -import io.restassured.response.Response; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.FilterableResponseSpecification; - -import org.springframework.restdocs.config.OperationPreprocessorsConfigurer; - -/** - * A configurer that can be used to configure the operation preprocessors when using REST - * Assured. - * - * @author Filip Hrisafov - * @since 2.0.0 - */ -public final class RestAssuredOperationPreprocessorsConfigurer extends - OperationPreprocessorsConfigurer - implements Filter { - - RestAssuredOperationPreprocessorsConfigurer(RestAssuredRestDocumentationConfigurer parent) { - super(parent); - } - - @Override - public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, - FilterContext context) { - return and().filter(requestSpec, responseSpec, context); - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRequestConverter.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRequestConverter.java deleted file mode 100644 index 091d51ecc..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRequestConverter.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import io.restassured.http.Cookie; -import io.restassured.http.Header; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.MultiPartSpecification; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationRequestPartFactory; -import org.springframework.restdocs.operation.RequestConverter; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.StreamUtils; -import org.springframework.util.StringUtils; - -/** - * A converter for creating an {@link OperationRequest} from a REST Assured - * {@link FilterableRequestSpecification}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -class RestAssuredRequestConverter implements RequestConverter { - - @Override - public OperationRequest convert(FilterableRequestSpecification requestSpec) { - return new OperationRequestFactory().create(URI.create(requestSpec.getURI()), - HttpMethod.valueOf(requestSpec.getMethod()), extractContent(requestSpec), extractHeaders(requestSpec), - extractParts(requestSpec), extractCookies(requestSpec)); - } - - private Collection extractCookies(FilterableRequestSpecification requestSpec) { - Collection cookies = new ArrayList<>(); - for (Cookie cookie : requestSpec.getCookies()) { - cookies.add(new RequestCookie(cookie.getName(), cookie.getValue())); - } - return cookies; - } - - private byte[] extractContent(FilterableRequestSpecification requestSpec) { - Object body = requestSpec.getBody(); - if (body != null) { - return convertContent(body); - } - StringBuilder parameters = new StringBuilder(); - if ("POST".equals(requestSpec.getMethod())) { - appendParameters(parameters, requestSpec.getRequestParams()); - } - if (!"GET".equals(requestSpec.getMethod())) { - appendParameters(parameters, requestSpec.getFormParams()); - } - return parameters.toString().getBytes(StandardCharsets.ISO_8859_1); - } - - private void appendParameters(StringBuilder content, Map parameters) { - for (Entry entry : parameters.entrySet()) { - String name = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof Iterable) { - for (Object v : (Iterable) value) { - append(content, name, v.toString()); - } - } - else if (value != null) { - append(content, name, value.toString()); - } - else { - append(content, name); - } - } - } - - private byte[] convertContent(Object content) { - if (content instanceof String) { - return ((String) content).getBytes(); - } - else if (content instanceof byte[]) { - return (byte[]) content; - } - else if (content instanceof File) { - return copyToByteArray((File) content); - } - else if (content instanceof InputStream) { - return copyToByteArray((InputStream) content); - } - else if (content == null) { - return new byte[0]; - } - else { - throw new IllegalStateException("Unsupported request content: " + content.getClass().getName()); - } - } - - private byte[] copyToByteArray(File file) { - try { - return FileCopyUtils.copyToByteArray(file); - } - catch (IOException ex) { - throw new IllegalStateException("Failed to read content from file " + file, ex); - } - } - - private byte[] copyToByteArray(InputStream inputStream) { - try { - inputStream.reset(); - } - catch (IOException ex) { - throw new IllegalStateException( - "Cannot read content from input stream " + inputStream + " due to reset() failure"); - } - try { - return StreamUtils.copyToByteArray(inputStream); - } - catch (IOException ex) { - throw new IllegalStateException("Failed to read content from input stream " + inputStream, ex); - } - } - - private HttpHeaders extractHeaders(FilterableRequestSpecification requestSpec) { - HttpHeaders httpHeaders = new HttpHeaders(); - for (Header header : requestSpec.getHeaders()) { - if (!isAllMediaTypesAcceptHeader(header)) { - httpHeaders.add(header.getName(), header.getValue()); - } - } - return httpHeaders; - } - - private boolean isAllMediaTypesAcceptHeader(Header header) { - return HttpHeaders.ACCEPT.equals(header.getName()) && "*/*".equals(header.getValue()); - } - - private Collection extractParts(FilterableRequestSpecification requestSpec) { - List parts = new ArrayList<>(); - for (MultiPartSpecification multiPartSpec : requestSpec.getMultiPartParams()) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType((multiPartSpec.getMimeType() != null) - ? MediaType.parseMediaType(multiPartSpec.getMimeType()) : MediaType.TEXT_PLAIN); - parts.add(new OperationRequestPartFactory().create(multiPartSpec.getControlName(), - multiPartSpec.getFileName(), convertContent(multiPartSpec.getContent()), headers)); - } - return parts; - } - - private static void append(StringBuilder sb, String key) { - append(sb, key, ""); - } - - private static void append(StringBuilder sb, String key, String value) { - doAppend(sb, urlEncode(key) + "=" + urlEncode(value)); - } - - private static void doAppend(StringBuilder sb, String toAppend) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(toAppend); - } - - private static String urlEncode(String s) { - if (!StringUtils.hasLength(s)) { - return ""; - } - return URLEncoder.encode(s, StandardCharsets.UTF_8); - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredResponseConverter.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredResponseConverter.java deleted file mode 100644 index a049ea1ae..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredResponseConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import io.restassured.http.Header; -import io.restassured.response.Response; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatusCode; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.ResponseConverter; -import org.springframework.restdocs.operation.ResponseCookie; - -/** - * A converter for creating an {@link OperationResponse} from a REST Assured - * {@link Response}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -class RestAssuredResponseConverter implements ResponseConverter { - - @Override - public OperationResponse convert(Response response) { - HttpHeaders headers = extractHeaders(response); - Collection cookies = extractCookies(response, headers); - return new OperationResponseFactory().create(HttpStatusCode.valueOf(response.getStatusCode()), - extractHeaders(response), extractContent(response), cookies); - } - - private Collection extractCookies(Response response, HttpHeaders headers) { - if (response.getCookies() == null || response.getCookies().size() == 0) { - return Collections.emptyList(); - } - List cookies = new ArrayList<>(); - for (Map.Entry cookie : response.getCookies().entrySet()) { - cookies.add(new ResponseCookie(cookie.getKey(), cookie.getValue())); - } - return cookies; - } - - private HttpHeaders extractHeaders(Response response) { - HttpHeaders httpHeaders = new HttpHeaders(); - for (Header header : response.getHeaders()) { - httpHeaders.add(header.getName(), header.getValue()); - } - return httpHeaders; - } - - private byte[] extractContent(Response response) { - return response.getBody().asByteArray(); - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java deleted file mode 100644 index a5309d49d..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentation.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.snippet.Snippet; - -/** - * Static factory methods for documenting RESTful APIs using REST Assured. - * - * @author Andy Wilkinson - * @since 1.2.0 - */ -public abstract class RestAssuredRestDocumentation { - - private static final RestAssuredRequestConverter REQUEST_CONVERTER = new RestAssuredRequestConverter(); - - private static final RestAssuredResponseConverter RESPONSE_CONVERTER = new RestAssuredResponseConverter(); - - private RestAssuredRestDocumentation() { - - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets}. - * @param identifier an identifier for the API call that is being documented - * @param snippets the snippets that will document the API call - * @return a {@link RestDocumentationFilter} that will produce the documentation - */ - public static RestDocumentationFilter document(String identifier, Snippet... snippets) { - return new RestDocumentationFilter( - new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, snippets)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code requestPreprocessor} is applied to the request before it is documented. - * @param identifier an identifier for the API call that is being documented - * @param requestPreprocessor the request preprocessor - * @param snippets the snippets - * @return a {@link RestDocumentationFilter} that will produce the documentation - */ - public static RestDocumentationFilter document(String identifier, OperationRequestPreprocessor requestPreprocessor, - Snippet... snippets) { - return new RestDocumentationFilter(new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, - RESPONSE_CONVERTER, requestPreprocessor, snippets)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code responsePreprocessor} is applied to the request before it is documented. - * @param identifier an identifier for the API call that is being documented - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - * @return a {@link RestDocumentationFilter} that will produce the documentation - */ - public static RestDocumentationFilter document(String identifier, - OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - return new RestDocumentationFilter(new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, - RESPONSE_CONVERTER, responsePreprocessor, snippets)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code requestPreprocessor} and {@code responsePreprocessor} are applied to the - * request and response respectively before they are documented. - * @param identifier an identifier for the API call that is being documented - * @param requestPreprocessor the request preprocessor - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - * @return a {@link RestDocumentationFilter} that will produce the documentation - */ - public static RestDocumentationFilter document(String identifier, OperationRequestPreprocessor requestPreprocessor, - OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - return new RestDocumentationFilter(new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, - RESPONSE_CONVERTER, requestPreprocessor, responsePreprocessor, snippets)); - } - - /** - * Provides access to a {@link RestAssuredRestDocumentationConfigurer} that can be - * used to configure Spring REST Docs using the given {@code contextProvider}. - * @param contextProvider the context provider - * @return the configurer - */ - public static RestAssuredRestDocumentationConfigurer documentationConfiguration( - RestDocumentationContextProvider contextProvider) { - return new RestAssuredRestDocumentationConfigurer(contextProvider); - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java deleted file mode 100644 index 401d02b8c..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurer.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.util.HashMap; -import java.util.Map; - -import io.restassured.filter.Filter; -import io.restassured.filter.FilterContext; -import io.restassured.response.Response; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.FilterableResponseSpecification; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.config.RestDocumentationConfigurer; - -/** - * A REST Assured-specific {@link RestDocumentationConfigurer}. - * - * @author Andy Wilkinson - * @author Filip Hrisafov - * @since 1.2.0 - */ -public final class RestAssuredRestDocumentationConfigurer extends - RestDocumentationConfigurer - implements Filter { - - private final RestAssuredSnippetConfigurer snippetConfigurer = new RestAssuredSnippetConfigurer(this); - - private final RestAssuredOperationPreprocessorsConfigurer operationPreprocessorsConfigurer = new RestAssuredOperationPreprocessorsConfigurer( - this); - - private final RestDocumentationContextProvider contextProvider; - - RestAssuredRestDocumentationConfigurer(RestDocumentationContextProvider contextProvider) { - this.contextProvider = contextProvider; - } - - @Override - public RestAssuredSnippetConfigurer snippets() { - return this.snippetConfigurer; - } - - @Override - public RestAssuredOperationPreprocessorsConfigurer operationPreprocessors() { - return this.operationPreprocessorsConfigurer; - } - - @Override - public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, - FilterContext filterContext) { - RestDocumentationContext context = this.contextProvider.beforeOperation(); - filterContext.setValue(RestDocumentationContext.class.getName(), context); - Map configuration = new HashMap<>(); - filterContext.setValue(RestDocumentationFilter.CONTEXT_KEY_CONFIGURATION, configuration); - apply(configuration, context); - return filterContext.next(requestSpec, responseSpec); - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredSnippetConfigurer.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredSnippetConfigurer.java deleted file mode 100644 index 250f61fb8..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestAssuredSnippetConfigurer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import io.restassured.filter.Filter; -import io.restassured.filter.FilterContext; -import io.restassured.response.Response; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.FilterableResponseSpecification; - -import org.springframework.restdocs.config.SnippetConfigurer; - -/** - * A configurer that can be used to configure the generated documentation snippets when - * using REST Assured. - * - * @author Andy Wilkinson - * @since 1.2.0 - */ -public final class RestAssuredSnippetConfigurer extends - SnippetConfigurer implements Filter { - - RestAssuredSnippetConfigurer(RestAssuredRestDocumentationConfigurer parent) { - super(parent); - } - - @Override - public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, - FilterContext context) { - return and().filter(requestSpec, responseSpec, context); - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java deleted file mode 100644 index e52b9c313..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.util.HashMap; -import java.util.Map; - -import io.restassured.filter.Filter; -import io.restassured.filter.FilterContext; -import io.restassured.response.Response; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.FilterableResponseSpecification; - -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.util.Assert; - -/** - * A REST Assured {@link Filter} for documenting RESTful APIs. - * - * @author Andy Wilkinson - * @since 1.2.0 - */ -public class RestDocumentationFilter implements Filter { - - static final String CONTEXT_KEY_CONFIGURATION = "org.springframework.restdocs.configuration"; - - private final RestDocumentationGenerator delegate; - - RestDocumentationFilter(RestDocumentationGenerator delegate) { - Assert.notNull(delegate, "delegate must be non-null"); - this.delegate = delegate; - } - - @Override - public final Response filter(FilterableRequestSpecification requestSpec, - FilterableResponseSpecification responseSpec, FilterContext context) { - Response response = context.next(requestSpec, responseSpec); - - Map configuration = getConfiguration(requestSpec, context); - - this.delegate.handle(requestSpec, response, configuration); - - return response; - } - - /** - * Returns the configuration that should be used when calling the delgate. The - * configuration is derived from the given {@code requestSpec} and {@code context}. - * @param requestSpec the request specification - * @param context the filter context - * @return the configuration - */ - protected Map getConfiguration(FilterableRequestSpecification requestSpec, FilterContext context) { - Map configuration = new HashMap<>(retrieveConfiguration(context)); - configuration.put(RestDocumentationContext.class.getName(), - context.getValue(RestDocumentationContext.class.getName())); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, requestSpec.getUserDefinedPath()); - return configuration; - } - - /** - * Creates a new {@link RestDocumentationFilter} that will produce documentation using - * the given {@code snippets}. - * @param snippets the snippets - * @return the new result handler - */ - public final RestDocumentationFilter document(Snippet... snippets) { - return new RestDocumentationFilter(this.delegate.withSnippets(snippets)) { - - @Override - protected Map getConfiguration(FilterableRequestSpecification requestSpec, - FilterContext context) { - Map configuration = super.getConfiguration(requestSpec, context); - configuration.remove(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS); - configuration.remove(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR); - configuration.remove(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR); - return configuration; - } - - }; - } - - private static Map retrieveConfiguration(FilterContext context) { - Map configuration = context.getValue(CONTEXT_KEY_CONFIGURATION); - Assert.state(configuration != null, - () -> "REST Docs configuration not found. Did you forget to add a " - + RestAssuredRestDocumentationConfigurer.class.getSimpleName() - + " as a filter when building the RequestSpecification?"); - return configuration; - } - -} diff --git a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/package-info.java b/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/package-info.java deleted file mode 100644 index 502b9a02c..000000000 --- a/spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Core classes for using Spring REST Docs with REST Assured. - */ -package org.springframework.restdocs.restassured; diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java deleted file mode 100644 index 1bef29294..000000000 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredParameterBehaviorTests.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; -import org.assertj.core.api.AbstractAssert; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests to verify that the understanding of REST Assured's parameter handling behavior is - * correct. - * - * @author Andy Wilkinson - */ -class RestAssuredParameterBehaviorTests { - - private static final MediaType APPLICATION_FORM_URLENCODED_ISO_8859_1 = MediaType - .parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=ISO-8859-1"); - - @RegisterExtension - public static TomcatServer tomcat = new TomcatServer(); - - private final RestAssuredRequestConverter factory = new RestAssuredRequestConverter(); - - private OperationRequest request; - - private RequestSpecification spec = RestAssured.given() - .port(tomcat.getPort()) - .filter((request, response, context) -> { - this.request = this.factory.convert(request); - return context.next(request, response); - }); - - @Test - void queryParameterOnGet() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .get("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.GET); - } - - @Test - void queryParameterOnHead() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .head("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.HEAD); - } - - @Test - void queryParameterOnPost() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .post("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.POST); - } - - @Test - void queryParameterOnPut() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .put("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.PUT); - } - - @Test - void queryParameterOnPatch() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .patch("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.PATCH); - } - - @Test - void queryParameterOnDelete() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .delete("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.DELETE); - } - - @Test - void queryParameterOnOptions() { - this.spec.queryParam("a", "alpha", "apple") - .queryParam("b", "bravo") - .options("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.OPTIONS); - } - - @Test - void paramOnGet() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").get("/query-parameter").then().statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.GET); - } - - @Test - void paramOnHead() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").head("/query-parameter").then().statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.HEAD); - } - - @Test - void paramOnPost() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").post("/form-url-encoded").then().statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.POST); - } - - @Test - void paramOnPut() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").put("/query-parameter").then().statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.PUT); - } - - @Test - void paramOnPatch() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").patch("/query-parameter").then().statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.PATCH); - } - - @Test - void paramOnDelete() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").delete("/query-parameter").then().statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.DELETE); - } - - @Test - void paramOnOptions() { - this.spec.param("a", "alpha", "apple").param("b", "bravo").options("/query-parameter").then().statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.OPTIONS); - } - - @Test - void formParamOnGet() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .get("/query-parameter") - .then() - .statusCode(200); - assertThatRequest(this.request).hasQueryParametersWithMethod(HttpMethod.GET); - } - - @Test - void formParamOnHead() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .head("/form-url-encoded") - .then() - .statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.HEAD); - } - - @Test - void formParamOnPost() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .post("/form-url-encoded") - .then() - .statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.POST); - } - - @Test - void formParamOnPut() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .put("/form-url-encoded") - .then() - .statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.PUT); - } - - @Test - void formParamOnPatch() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .patch("/form-url-encoded") - .then() - .statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.PATCH); - } - - @Test - void formParamOnDelete() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .delete("/form-url-encoded") - .then() - .statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.DELETE); - } - - @Test - void formParamOnOptions() { - this.spec.formParam("a", "alpha", "apple") - .formParam("b", "bravo") - .options("/form-url-encoded") - .then() - .statusCode(200); - assertThatRequest(this.request).isFormUrlEncodedWithMethod(HttpMethod.OPTIONS); - } - - private OperationRequestAssert assertThatRequest(OperationRequest request) { - return new OperationRequestAssert(request); - } - - private static final class OperationRequestAssert extends AbstractAssert { - - private OperationRequestAssert(OperationRequest actual) { - super(actual, OperationRequestAssert.class); - } - - private void isFormUrlEncodedWithMethod(HttpMethod method) { - assertThat(this.actual.getMethod()).isEqualTo(method); - assertThat(this.actual.getUri().getRawQuery()).isNull(); - assertThat(this.actual.getContentAsString()).isEqualTo("a=alpha&a=apple&b=bravo"); - assertThat(this.actual.getHeaders().getContentType()).isEqualTo(APPLICATION_FORM_URLENCODED_ISO_8859_1); - } - - private void hasQueryParametersWithMethod(HttpMethod method) { - assertThat(this.actual.getMethod()).isEqualTo(method); - assertThat(this.actual.getUri().getRawQuery()).isEqualTo("a=alpha&a=apple&b=bravo"); - assertThat(this.actual.getContentAsString()).isEmpty(); - } - - } - -} diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java deleted file mode 100644 index 09d411ae2..000000000 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRequestConverterTests.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.net.URI; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; - -import io.restassured.RestAssured; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.RequestCookie; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.entry; - -/** - * Tests for {@link RestAssuredRequestConverter}. - * - * @author Andy Wilkinson - */ -class RestAssuredRequestConverterTests { - - @RegisterExtension - public static TomcatServer tomcat = new TomcatServer(); - - private final RestAssuredRequestConverter factory = new RestAssuredRequestConverter(); - - @Test - void requestUri() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()); - requestSpec.get("/foo/bar"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost:" + tomcat.getPort() + "/foo/bar")); - } - - @Test - void requestMethod() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()); - requestSpec.head("/foo/bar"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getMethod()).isEqualTo(HttpMethod.HEAD); - } - - @Test - void queryStringParameters() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).queryParam("foo", "bar"); - requestSpec.get("/"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getUri().getRawQuery()).isEqualTo("foo=bar"); - } - - @Test - void queryStringFromUrlParameters() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()); - requestSpec.get("/?foo=bar&foo=qix"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getUri().getRawQuery()).isEqualTo("foo=bar&foo=qix"); - } - - @Test - void paramOnGetRequestIsMappedToQueryString() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).param("foo", "bar"); - requestSpec.get("/"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getUri().getRawQuery()).isEqualTo("foo=bar"); - } - - @Test - void headers() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).header("Foo", "bar"); - requestSpec.get("/"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getHeaders().headerSet()).containsOnly(entry("Foo", Collections.singletonList("bar")), - entry("Host", Collections.singletonList("localhost:" + tomcat.getPort()))); - } - - @Test - void headersWithCustomAccept() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .header("Foo", "bar") - .accept("application/json"); - requestSpec.get("/"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getHeaders().headerSet()).containsOnly(entry("Foo", Collections.singletonList("bar")), - entry("Accept", Collections.singletonList("application/json")), - entry("Host", Collections.singletonList("localhost:" + tomcat.getPort()))); - } - - @Test - void cookies() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .cookie("cookie1", "cookieVal1") - .cookie("cookie2", "cookieVal2"); - requestSpec.get("/"); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getCookies().size()).isEqualTo(2); - - Iterator cookieIterator = request.getCookies().iterator(); - RequestCookie cookie1 = cookieIterator.next(); - - assertThat(cookie1.getName()).isEqualTo("cookie1"); - assertThat(cookie1.getValue()).isEqualTo("cookieVal1"); - - RequestCookie cookie2 = cookieIterator.next(); - assertThat(cookie2.getName()).isEqualTo("cookie2"); - assertThat(cookie2.getValue()).isEqualTo("cookieVal2"); - } - - @Test - void multipart() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .multiPart("a", "a.txt", "alpha", null) - .multiPart("b", new ObjectBody("bar"), "application/json"); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - Collection parts = request.getParts(); - assertThat(parts).hasSize(2); - assertThat(parts).extracting("name").containsExactly("a", "b"); - assertThat(parts).extracting("submittedFileName").containsExactly("a.txt", "file"); - assertThat(parts).extracting("contentAsString").containsExactly("alpha", "{\"foo\":\"bar\"}"); - assertThat(parts).map((part) -> part.getHeaders().get(HttpHeaders.CONTENT_TYPE)) - .containsExactly(Collections.singletonList(MediaType.TEXT_PLAIN_VALUE), - Collections.singletonList(MediaType.APPLICATION_JSON_VALUE)); - } - - @Test - void byteArrayBody() { - RequestSpecification requestSpec = RestAssured.given().body("body".getBytes()).port(tomcat.getPort()); - requestSpec.post(); - this.factory.convert((FilterableRequestSpecification) requestSpec); - } - - @Test - void stringBody() { - RequestSpecification requestSpec = RestAssured.given().body("body").port(tomcat.getPort()); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getContentAsString()).isEqualTo("body"); - } - - @Test - void objectBody() { - RequestSpecification requestSpec = RestAssured.given().body(new ObjectBody("bar")).port(tomcat.getPort()); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getContentAsString()).isEqualTo("{\"foo\":\"bar\"}"); - } - - @Test - void byteArrayInputStreamBody() { - RequestSpecification requestSpec = RestAssured.given() - .body(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 })) - .port(tomcat.getPort()); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getContent()).isEqualTo(new byte[] { 1, 2, 3, 4 }); - } - - @Test - void fileBody() { - RequestSpecification requestSpec = RestAssured.given() - .body(new File("src/test/resources/body.txt")) - .port(tomcat.getPort()); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getContentAsString()).isEqualTo("file"); - } - - @Test - void fileInputStreamBody() throws FileNotFoundException { - FileInputStream inputStream = new FileInputStream("src/test/resources/body.txt"); - RequestSpecification requestSpec = RestAssured.given().body(inputStream).port(tomcat.getPort()); - requestSpec.post(); - assertThatIllegalStateException() - .isThrownBy(() -> this.factory.convert((FilterableRequestSpecification) requestSpec)) - .withMessage("Cannot read content from input stream " + inputStream + " due to reset() failure"); - } - - @Test - void multipartWithByteArrayInputStreamBody() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .multiPart("foo", "foo.txt", new ByteArrayInputStream("foo".getBytes())); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("foo"); - } - - @Test - void multipartWithStringBody() { - RequestSpecification requestSpec = RestAssured.given().port(tomcat.getPort()).multiPart("control", "foo"); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("foo"); - } - - @Test - void multipartWithByteArrayBody() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .multiPart("control", "file", "foo".getBytes()); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("foo"); - } - - @Test - void multipartWithFileBody() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .multiPart(new File("src/test/resources/body.txt")); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("file"); - } - - @Test - void multipartWithFileInputStreamBody() throws FileNotFoundException { - FileInputStream inputStream = new FileInputStream("src/test/resources/body.txt"); - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .multiPart("foo", "foo.txt", inputStream); - requestSpec.post(); - assertThatIllegalStateException() - .isThrownBy(() -> this.factory.convert((FilterableRequestSpecification) requestSpec)) - .withMessage("Cannot read content from input stream " + inputStream + " due to reset() failure"); - } - - @Test - void multipartWithObjectBody() { - RequestSpecification requestSpec = RestAssured.given() - .port(tomcat.getPort()) - .multiPart("control", new ObjectBody("bar")); - requestSpec.post(); - OperationRequest request = this.factory.convert((FilterableRequestSpecification) requestSpec); - assertThat(request.getParts().iterator().next().getContentAsString()).isEqualTo("{\"foo\":\"bar\"}"); - } - - /** - * Sample object body to verify JSON serialization. - */ - public static class ObjectBody { - - private final String foo; - - ObjectBody(String foo) { - this.foo = foo; - } - - public String getFoo() { - return this.foo; - } - - } - -} diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java deleted file mode 100644 index 22b17601b..000000000 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredResponseConverterTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import io.restassured.http.Headers; -import io.restassured.response.Response; -import io.restassured.response.ResponseBody; -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpStatusCode; -import org.springframework.restdocs.operation.OperationResponse; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link RestAssuredResponseConverter}. - * - * @author Andy Wilkinson - */ -class RestAssuredResponseConverterTests { - - private final RestAssuredResponseConverter converter = new RestAssuredResponseConverter(); - - @Test - void responseWithCustomStatus() { - Response response = mock(Response.class); - given(response.getStatusCode()).willReturn(600); - given(response.getHeaders()).willReturn(new Headers()); - ResponseBody body = mock(ResponseBody.class); - given(response.getBody()).willReturn(body); - given(body.asByteArray()).willReturn(new byte[0]); - OperationResponse operationResponse = this.converter.convert(response); - assertThat(operationResponse.getStatus()).isEqualTo(HttpStatusCode.valueOf(600)); - } - -} diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java deleted file mode 100644 index 8dbf59354..000000000 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationConfigurerTests.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.util.List; -import java.util.Map; - -import io.restassured.filter.FilterContext; -import io.restassured.specification.FilterableRequestSpecification; -import io.restassured.specification.FilterableResponseSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.operation.preprocess.Preprocessors; -import org.springframework.restdocs.snippet.WriterResolver; -import org.springframework.restdocs.templates.TemplateEngine; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link RestAssuredRestDocumentationConfigurer}. - * - * @author Andy Wilkinson - * @author Filip Hrisafov - */ -@ExtendWith(RestDocumentationExtension.class) -class RestAssuredRestDocumentationConfigurerTests { - - private final FilterableRequestSpecification requestSpec = mock(FilterableRequestSpecification.class); - - private final FilterableResponseSpecification responseSpec = mock(FilterableResponseSpecification.class); - - private final FilterContext filterContext = mock(FilterContext.class); - - private RestAssuredRestDocumentationConfigurer configurer; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.configurer = new RestAssuredRestDocumentationConfigurer(restDocumentation); - } - - @Test - void nextFilterIsCalled() { - this.configurer.filter(this.requestSpec, this.responseSpec, this.filterContext); - verify(this.filterContext).next(this.requestSpec, this.responseSpec); - } - - @Test - void configurationIsAddedToTheContext() { - this.configurer.operationPreprocessors() - .withRequestDefaults(Preprocessors.prettyPrint()) - .withResponseDefaults(Preprocessors.modifyHeaders().remove("Foo")) - .filter(this.requestSpec, this.responseSpec, this.filterContext); - @SuppressWarnings("rawtypes") - ArgumentCaptor configurationCaptor = ArgumentCaptor.forClass(Map.class); - verify(this.filterContext).setValue(eq(RestDocumentationFilter.CONTEXT_KEY_CONFIGURATION), - configurationCaptor.capture()); - @SuppressWarnings("unchecked") - Map configuration = configurationCaptor.getValue(); - assertThat(configuration.get(TemplateEngine.class.getName())).isInstanceOf(TemplateEngine.class); - assertThat(configuration.get(WriterResolver.class.getName())).isInstanceOf(WriterResolver.class); - assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS)) - .isInstanceOf(List.class); - assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR)) - .isInstanceOf(OperationRequestPreprocessor.class); - assertThat(configuration.get(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR)) - .isInstanceOf(OperationResponsePreprocessor.class); - } - -} diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java deleted file mode 100644 index 89d9c3516..000000000 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.charset.StandardCharsets; -import java.util.regex.Pattern; - -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.assertj.core.api.Condition; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.SnippetConditions; -import org.springframework.restdocs.testfixtures.SnippetConditions.CodeBlockCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpRequestCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition; -import org.springframework.util.FileCopyUtils; -import org.springframework.web.bind.annotation.RequestMethod; - -import static io.restassured.RestAssured.given; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.fail; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.maskLinks; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyHeaders; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.restdocs.request.RequestDocumentation.requestParts; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; -import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration; - -/** - * Integration tests for using Spring REST Docs with REST Assured. - * - * @author Andy Wilkinson - * @author Tomasz Kopczynski - * @author Filip Hrisafov - */ -@ExtendWith(RestDocumentationExtension.class) -class RestAssuredRestDocumentationIntegrationTests { - - @RegisterExtension - private static TomcatServer tomcat = new TomcatServer(); - - @Test - void defaultSnippetGeneration(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("default")) - .get("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/default"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - } - - @Test - void curlSnippetWithContent(RestDocumentationContextProvider restDocumentation) { - String contentType = "text/plain; charset=UTF-8"; - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("curl-snippet-with-content")) - .accept("application/json") - .body("content") - .contentType(contentType) - .post("/") - .then() - .statusCode(200); - - assertThat(new File("build/generated-snippets/curl-snippet-with-content/curl-request.adoc")).has(content( - codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String.format("$ curl 'http://localhost:" - + tomcat.getPort() + "/' -i -X POST \\%n" + " -H 'Accept: application/json' \\%n" - + " -H 'Content-Type: " + contentType + "' \\%n" + " -d 'content'")))); - } - - @Test - void curlSnippetWithCookies(RestDocumentationContextProvider restDocumentation) { - String contentType = "text/plain; charset=UTF-8"; - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("curl-snippet-with-cookies")) - .accept("application/json") - .contentType(contentType) - .cookie("cookieName", "cookieVal") - .get("/") - .then() - .statusCode(200); - assertThat(new File("build/generated-snippets/curl-snippet-with-cookies/curl-request.adoc")).has(content( - codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String.format("$ curl 'http://localhost:" - + tomcat.getPort() + "/' -i -X GET \\%n" + " -H 'Accept: application/json' \\%n" - + " -H 'Content-Type: " + contentType + "' \\%n" + " --cookie 'cookieName=cookieVal'")))); - } - - @Test - void curlSnippetWithEmptyParameterQueryString(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("curl-snippet-with-empty-parameter-query-string")) - .accept("application/json") - .param("a", "") - .get("/") - .then() - .statusCode(200); - assertThat( - new File("build/generated-snippets/curl-snippet-with-empty-parameter-query-string/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl 'http://localhost:" + tomcat.getPort() - + "/?a=' -i -X GET \\%n -H 'Accept: application/json'")))); - } - - @Test - void curlSnippetWithQueryStringOnPost(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("curl-snippet-with-query-string")) - .accept("application/json") - .param("foo", "bar") - .param("a", "alpha") - .post("/?foo=bar") - .then() - .statusCode(200); - String contentType = "application/x-www-form-urlencoded; charset=ISO-8859-1"; - assertThat(new File("build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl " + "'http://localhost:" + tomcat.getPort() - + "/?foo=bar' -i -X POST \\%n" + " -H 'Accept: application/json' \\%n" - + " -H 'Content-Type: " + contentType + "' \\%n" + " -d 'foo=bar&a=alpha'")))); - } - - @Test - void linksSnippet(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("links", links(linkWithRel("rel").description("The description")))) - .accept("application/json") - .get("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "links.adoc"); - } - - @Test - void pathParametersSnippet(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("path-parameters", - pathParameters(parameterWithName("foo").description("The description")))) - .accept("application/json") - .get("/{foo}", "") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/path-parameters"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "path-parameters.adoc"); - } - - @Test - void queryParametersSnippet(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("query-parameters", - queryParameters(parameterWithName("foo").description("The description")))) - .accept("application/json") - .param("foo", "bar") - .get("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/query-parameters"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "query-parameters.adoc"); - } - - @Test - void requestFieldsSnippet(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("request-fields", requestFields(fieldWithPath("a").description("The description")))) - .accept("application/json") - .body("{\"a\":\"alpha\"}") - .post("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/request-fields"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "request-fields.adoc"); - } - - @Test - void requestPartsSnippet(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("request-parts", requestParts(partWithName("a").description("The description")))) - .multiPart("a", "foo") - .post("/upload") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/request-parts"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "request-parts.adoc"); - } - - @Test - void responseFieldsSnippet(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("response-fields", - responseFields(fieldWithPath("a").description("The description"), - subsectionWithPath("links").description("Links to other resources")))) - .accept("application/json") - .get("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/response-fields"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc", "response-fields.adoc"); - } - - @Test - void parameterizedOutputDirectory(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("{method-name}")) - .get("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/parameterized-output-directory"), - "http-request.adoc", "http-response.adoc", "curl-request.adoc"); - } - - @Test - void multiStep(RestDocumentationContextProvider restDocumentation) { - RequestSpecification spec = new RequestSpecBuilder().setPort(tomcat.getPort()) - .addFilter(documentationConfiguration(restDocumentation)) - .addFilter(document("{method-name}-{step}")) - .build(); - given(spec).get("/").then().statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-1/"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - given(spec).get("/").then().statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-2/"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - given(spec).get("/").then().statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/multi-step-3/"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - } - - @Test - void additionalSnippets(RestDocumentationContextProvider restDocumentation) { - RestDocumentationFilter documentation = document("{method-name}-{step}"); - RequestSpecification spec = new RequestSpecBuilder().setPort(tomcat.getPort()) - .addFilter(documentationConfiguration(restDocumentation)) - .addFilter(documentation) - .build(); - given(spec) - .filter(documentation.document( - responseHeaders(headerWithName("a").description("one"), headerWithName("Foo").description("two")))) - .get("/") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/additional-snippets-1/"), - "http-request.adoc", "http-response.adoc", "curl-request.adoc", "response-headers.adoc"); - } - - @Test - void responseWithCookie(RestDocumentationContextProvider restDocumentation) { - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("set-cookie", - preprocessResponse(modifyHeaders().remove(HttpHeaders.DATE).remove(HttpHeaders.CONTENT_TYPE)))) - .get("/set-cookie") - .then() - .statusCode(200); - assertExpectedSnippetFilesExist(new File("build/generated-snippets/set-cookie"), "http-request.adoc", - "http-response.adoc", "curl-request.adoc"); - assertThat(new File("build/generated-snippets/set-cookie/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK) - .header(HttpHeaders.SET_COOKIE, "name=value; Domain=localhost; HttpOnly") - .header("Keep-Alive", "timeout=60") - .header("Connection", "keep-alive"))); - } - - @Test - void preprocessedRequest(RestDocumentationContextProvider restDocumentation) { - Pattern pattern = Pattern.compile("(\"alpha\")"); - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .header("a", "alpha") - .header("b", "bravo") - .contentType("application/json") - .accept("application/json") - .body("{\"a\":\"alpha\"}") - .filter(document("original-request")) - .filter(document("preprocessed-request", - preprocessRequest(prettyPrint(), replacePattern(pattern, "\"<>\""), modifyUris().removePort(), - modifyHeaders().remove("a").remove(HttpHeaders.CONTENT_LENGTH)))) - .get("/") - .then() - .statusCode(200); - assertThat(new File("build/generated-snippets/original-request/http-request.adoc")) - .has(content(httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/").header("a", "alpha") - .header("b", "bravo") - .header("Accept", MediaType.APPLICATION_JSON_VALUE) - .header("Content-Type", "application/json") - .header("Host", "localhost:" + tomcat.getPort()) - .header("Content-Length", "13") - .content("{\"a\":\"alpha\"}"))); - String prettyPrinted = String.format("{%n \"a\" : \"<>\"%n}"); - assertThat(new File("build/generated-snippets/preprocessed-request/http-request.adoc")) - .has(content(httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/").header("b", "bravo") - .header("Accept", MediaType.APPLICATION_JSON_VALUE) - .header("Content-Type", "application/json") - .header("Host", "localhost") - .content(prettyPrinted))); - } - - @Test - void defaultPreprocessedRequest(RestDocumentationContextProvider restDocumentation) { - Pattern pattern = Pattern.compile("(\"alpha\")"); - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation).operationPreprocessors() - .withRequestDefaults(prettyPrint(), replacePattern(pattern, "\"<>\""), modifyUris().removePort(), - modifyHeaders().remove("a").remove(HttpHeaders.CONTENT_LENGTH))) - .header("a", "alpha") - .header("b", "bravo") - .contentType("application/json") - .accept("application/json") - .body("{\"a\":\"alpha\"}") - .filter(document("default-preprocessed-request")) - .get("/") - .then() - .statusCode(200); - String prettyPrinted = String.format("{%n \"a\" : \"<>\"%n}"); - assertThat(new File("build/generated-snippets/default-preprocessed-request/http-request.adoc")) - .has(content(httpRequest(TemplateFormats.asciidoctor(), RequestMethod.GET, "/").header("b", "bravo") - .header("Accept", MediaType.APPLICATION_JSON_VALUE) - .header("Content-Type", "application/json") - .header("Host", "localhost") - .content(prettyPrinted))); - } - - @Test - void preprocessedResponse(RestDocumentationContextProvider restDocumentation) { - Pattern pattern = Pattern.compile("(\"alpha\")"); - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation)) - .filter(document("original-response")) - .filter(document("preprocessed-response", - preprocessResponse(prettyPrint(), maskLinks(), - modifyHeaders().remove("a").remove("Transfer-Encoding").remove("Date").remove("Server"), - replacePattern(pattern, "\"<>\""), - modifyUris().scheme("https").host("api.example.com").removePort()))) - .get("/") - .then() - .statusCode(200); - String prettyPrinted = String.format("{%n \"a\" : \"<>\",%n \"links\" : " - + "[ {%n \"rel\" : \"rel\",%n \"href\" : \"...\"%n } ]%n}"); - assertThat(new File("build/generated-snippets/preprocessed-response/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK) - .header("Foo", "https://api.example.com/foo/bar") - .header("Content-Type", "application/json;charset=UTF-8") - .header("Keep-Alive", "timeout=60") - .header("Connection", "keep-alive") - .header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length) - .content(prettyPrinted))); - } - - @Test - void defaultPreprocessedResponse(RestDocumentationContextProvider restDocumentation) { - Pattern pattern = Pattern.compile("(\"alpha\")"); - given().port(tomcat.getPort()) - .filter(documentationConfiguration(restDocumentation).operationPreprocessors() - .withResponseDefaults(prettyPrint(), maskLinks(), - modifyHeaders().remove("a").remove("Transfer-Encoding").remove("Date").remove("Server"), - replacePattern(pattern, "\"<>\""), - modifyUris().scheme("https").host("api.example.com").removePort())) - .filter(document("default-preprocessed-response")) - .get("/") - .then() - .statusCode(200); - String prettyPrinted = String.format("{%n \"a\" : \"<>\",%n \"links\" : " - + "[ {%n \"rel\" : \"rel\",%n \"href\" : \"...\"%n } ]%n}"); - assertThat(new File("build/generated-snippets/default-preprocessed-response/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK) - .header("Foo", "https://api.example.com/foo/bar") - .header("Content-Type", "application/json;charset=UTF-8") - .header("Keep-Alive", "timeout=60") - .header("Connection", "keep-alive") - .header(HttpHeaders.CONTENT_LENGTH, prettyPrinted.getBytes().length) - .content(prettyPrinted))); - } - - @Test - void customSnippetTemplate(RestDocumentationContextProvider restDocumentation) throws MalformedURLException { - ClassLoader classLoader = new URLClassLoader( - new URL[] { new File("src/test/resources/custom-snippet-templates").toURI().toURL() }, - getClass().getClassLoader()); - ClassLoader previous = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(classLoader); - try { - given().port(tomcat.getPort()) - .accept("application/json") - .filter(documentationConfiguration(restDocumentation)) - .filter(document("custom-snippet-template")) - .get("/") - .then() - .statusCode(200); - } - finally { - Thread.currentThread().setContextClassLoader(previous); - } - assertThat(new File("build/generated-snippets/custom-snippet-template/curl-request.adoc")) - .hasContent("Custom curl request"); - } - - @Test - void exceptionShouldBeThrownWhenCallDocumentRequestSpecificationNotConfigured() { - assertThatThrownBy(() -> given().port(tomcat.getPort()).filter(document("default")).get("/")) - .isInstanceOf(IllegalStateException.class) - .hasMessage("REST Docs configuration not found. Did you forget to add a " - + "RestAssuredRestDocumentationConfigurer as a filter when building the RequestSpecification?"); - } - - @Test - void exceptionShouldBeThrownWhenCallDocumentSnippetsRequestSpecificationNotConfigured() { - RestDocumentationFilter documentation = document("{method-name}-{step}"); - assertThatThrownBy(() -> given().port(tomcat.getPort()) - .filter(documentation.document(responseHeaders(headerWithName("a").description("one")))) - .get("/")) - .isInstanceOf(IllegalStateException.class) - .hasMessage("REST Docs configuration not found. Did you forget to add a " - + "RestAssuredRestDocumentationConfigurer as a filter when building the " - + "RequestSpecification?"); - } - - private void assertExpectedSnippetFilesExist(File directory, String... snippets) { - for (String snippet : snippets) { - assertThat(new File(directory, snippet)).isFile(); - } - } - - private Condition content(final Condition delegate) { - return new Condition<>() { - - @Override - public boolean matches(File value) { - try { - String copyToString = FileCopyUtils - .copyToString(new InputStreamReader(new FileInputStream(value), StandardCharsets.UTF_8)); - System.out.println(copyToString); - return delegate.matches(copyToString); - } - catch (IOException ex) { - fail("Failed to read '" + value + "'", ex); - return false; - } - } - - }; - } - - private CodeBlockCondition codeBlock(TemplateFormat format, String language) { - return SnippetConditions.codeBlock(format, language); - } - - private HttpRequestCondition httpRequest(TemplateFormat format, RequestMethod requestMethod, String uri) { - return SnippetConditions.httpRequest(format, requestMethod, uri); - } - - private HttpResponseCondition httpResponse(TemplateFormat format, HttpStatus status) { - return SnippetConditions.httpResponse(format, status); - } - -} diff --git a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java b/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java deleted file mode 100644 index f91e1943c..000000000 --- a/spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/TomcatServer.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.restassured; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.catalina.Context; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.startup.Tomcat; -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.Extension; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; -import org.junit.jupiter.api.extension.ExtensionContext.Store; - -import org.springframework.http.MediaType; -import org.springframework.util.FileCopyUtils; - -/** - * {@link Extension} that starts and stops a Tomcat server. - * - * @author Andy Wilkinson - */ -class TomcatServer implements BeforeAllCallback, AfterAllCallback { - - private int port; - - @Override - public void beforeAll(ExtensionContext extensionContext) { - Store store = extensionContext.getStore(Namespace.create(TomcatServer.class)); - store.getOrComputeIfAbsent(Tomcat.class, (key) -> { - Tomcat tomcat = new Tomcat(); - tomcat.getConnector().setPort(0); - Context context = tomcat.addContext("/", null); - tomcat.addServlet("/", "test", new TestServlet()); - context.addServletMappingDecoded("/", "test"); - tomcat.addServlet("/", "set-cookie", new CookiesServlet()); - context.addServletMappingDecoded("/set-cookie", "set-cookie"); - tomcat.addServlet("/", "query-parameter", new QueryParameterServlet()); - context.addServletMappingDecoded("/query-parameter", "query-parameter"); - tomcat.addServlet("/", "form-url-encoded", new FormUrlEncodedServlet()); - context.addServletMappingDecoded("/form-url-encoded", "form-url-encoded"); - try { - tomcat.start(); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - this.port = tomcat.getConnector().getLocalPort(); - return tomcat; - }); - } - - @Override - public void afterAll(ExtensionContext extensionContext) throws LifecycleException { - Store store = extensionContext.getStore(Namespace.create(TomcatServer.class)); - Tomcat tomcat = store.get(Tomcat.class, Tomcat.class); - if (tomcat != null) { - tomcat.stop(); - } - } - - int getPort() { - return this.port; - } - - /** - * {@link HttpServlet} used to handle requests in the tests. - */ - private static final class TestServlet extends HttpServlet { - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - respondWithJson(response); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - respondWithJson(response); - } - - private void respondWithJson(HttpServletResponse response) throws IOException, JsonProcessingException { - response.setCharacterEncoding("UTF-8"); - response.setContentType("application/json"); - Map content = new HashMap<>(); - content.put("a", "alpha"); - Map link = new HashMap<>(); - link.put("rel", "rel"); - link.put("href", "href"); - content.put("links", Arrays.asList(link)); - response.getWriter().println(new ObjectMapper().writeValueAsString(content)); - response.setHeader("a", "alpha"); - response.setHeader("Foo", "http://localhost:12345/foo/bar"); - response.flushBuffer(); - } - - } - - /** - * {@link HttpServlet} used to handle cookies-related requests in the tests. - */ - private static final class CookiesServlet extends HttpServlet { - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Cookie cookie = new Cookie("name", "value"); - cookie.setDomain("localhost"); - cookie.setHttpOnly(true); - - resp.addCookie(cookie); - } - - } - - private static final class QueryParameterServlet extends HttpServlet { - - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (!req.getQueryString().equals("a=alpha&a=apple&b=bravo")) { - throw new ServletException("Incorrect query string"); - } - resp.setStatus(200); - } - - } - - private static final class FormUrlEncodedServlet extends HttpServlet { - - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (!MediaType.APPLICATION_FORM_URLENCODED - .isCompatibleWith(MediaType.parseMediaType(req.getContentType()))) { - throw new ServletException("Incorrect Content-Type"); - } - String content = FileCopyUtils.copyToString(new InputStreamReader(req.getInputStream())); - if (!"a=alpha&a=apple&b=bravo".equals(content)) { - throw new ServletException("Incorrect body content"); - } - resp.setStatus(200); - } - - } - -} diff --git a/spring-restdocs-restassured/src/test/resources/body.txt b/spring-restdocs-restassured/src/test/resources/body.txt deleted file mode 100644 index 1a010b1c0..000000000 --- a/spring-restdocs-restassured/src/test/resources/body.txt +++ /dev/null @@ -1 +0,0 @@ -file \ No newline at end of file diff --git a/spring-restdocs-restassured/src/test/resources/custom-snippet-templates/org/springframework/restdocs/templates/curl-request.snippet b/spring-restdocs-restassured/src/test/resources/custom-snippet-templates/org/springframework/restdocs/templates/curl-request.snippet deleted file mode 100644 index 07f3a48ff..000000000 --- a/spring-restdocs-restassured/src/test/resources/custom-snippet-templates/org/springframework/restdocs/templates/curl-request.snippet +++ /dev/null @@ -1 +0,0 @@ -Custom curl request \ No newline at end of file diff --git a/spring-restdocs-webtestclient/build.gradle b/spring-restdocs-webtestclient/build.gradle deleted file mode 100644 index 7675c0d6c..000000000 --- a/spring-restdocs-webtestclient/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id "java-library" - id "maven-publish" -} - -description = "Spring REST Docs WebFlux" - -dependencies { - api(project(":spring-restdocs-core")) - api("org.springframework:spring-test") - api("org.springframework:spring-webflux") - - compileOnly("org.hamcrest:hamcrest-core") - - internal(platform(project(":spring-restdocs-platform"))) - - testCompileOnly("org.hamcrest:hamcrest-core") - - testImplementation(testFixtures(project(":spring-restdocs-core"))) - - testRuntimeOnly("org.springframework:spring-context") -} - -tasks.named("test") { - useJUnitPlatform(); -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientOperationPreprocessorsConfigurer.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientOperationPreprocessorsConfigurer.java deleted file mode 100644 index 6c612acf7..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientOperationPreprocessorsConfigurer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import reactor.core.publisher.Mono; - -import org.springframework.restdocs.config.OperationPreprocessorsConfigurer; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeFunction; - -/** - * A configurer that can be used to configure the operation preprocessors. - * - * @author Andy Wilkinson - * @since 2.0.0 - */ -public final class WebTestClientOperationPreprocessorsConfigurer extends - OperationPreprocessorsConfigurer - implements ExchangeFilterFunction { - - WebTestClientOperationPreprocessorsConfigurer(WebTestClientRestDocumentationConfigurer parent) { - super(parent); - } - - @Override - public Mono filter(ClientRequest request, ExchangeFunction next) { - return and().filter(request, next); - } - -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverter.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverter.java deleted file mode 100644 index 7fef86918..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverter.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.io.ByteArrayOutputStream; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import reactor.core.publisher.Flux; - -import org.springframework.core.ResolvableType; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.core.io.buffer.DefaultDataBuffer; -import org.springframework.core.io.buffer.DefaultDataBufferFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ReactiveHttpInputMessage; -import org.springframework.http.codec.HttpMessageReader; -import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; -import org.springframework.http.codec.multipart.FilePart; -import org.springframework.http.codec.multipart.MultipartHttpMessageReader; -import org.springframework.http.codec.multipart.Part; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestFactory; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.restdocs.operation.OperationRequestPartFactory; -import org.springframework.restdocs.operation.RequestConverter; -import org.springframework.restdocs.operation.RequestCookie; -import org.springframework.test.web.reactive.server.ExchangeResult; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.LinkedMultiValueMap; - -/** - * A {@link RequestConverter} for creating an {@link OperationRequest} derived from an - * {@link ExchangeResult}. - * - * @author Andy Wilkinson - */ -class WebTestClientRequestConverter implements RequestConverter { - - @Override - public OperationRequest convert(ExchangeResult result) { - HttpHeaders headers = extractRequestHeaders(result); - return new OperationRequestFactory().create(result.getUrl(), result.getMethod(), result.getRequestBodyContent(), - headers, extractRequestParts(result), extractCookies(headers)); - } - - private HttpHeaders extractRequestHeaders(ExchangeResult result) { - HttpHeaders extracted = new HttpHeaders(); - extracted.putAll(result.getRequestHeaders()); - extracted.remove(WebTestClient.WEBTESTCLIENT_REQUEST_ID); - return extracted; - } - - private List extractRequestParts(ExchangeResult result) { - HttpMessageReader partHttpMessageReader = new DefaultPartHttpMessageReader(); - return new MultipartHttpMessageReader(partHttpMessageReader) - .readMono(ResolvableType.forClass(Part.class), new ExchangeResultReactiveHttpInputMessage(result), - Collections.emptyMap()) - .onErrorReturn(new LinkedMultiValueMap<>()) - .block() - .values() - .stream() - .flatMap((parts) -> parts.stream().map(this::createOperationRequestPart)) - .collect(Collectors.toList()); - } - - private OperationRequestPart createOperationRequestPart(Part part) { - ByteArrayOutputStream content = readPartBodyContent(part); - return new OperationRequestPartFactory().create(part.name(), - (part instanceof FilePart) ? ((FilePart) part).filename() : null, content.toByteArray(), - part.headers()); - } - - private ByteArrayOutputStream readPartBodyContent(Part part) { - ByteArrayOutputStream contentStream = new ByteArrayOutputStream(); - DataBufferUtils.write(part.content(), contentStream).blockFirst(); - return contentStream; - } - - private Collection extractCookies(HttpHeaders headers) { - List cookieHeaders = headers.get(HttpHeaders.COOKIE); - if (cookieHeaders == null) { - return Collections.emptyList(); - } - headers.remove(HttpHeaders.COOKIE); - return cookieHeaders.stream().map(this::createRequestCookie).collect(Collectors.toList()); - } - - private RequestCookie createRequestCookie(String header) { - String[] components = header.split("="); - return new RequestCookie(components[0], components[1]); - } - - private final class ExchangeResultReactiveHttpInputMessage implements ReactiveHttpInputMessage { - - private final ExchangeResult result; - - private ExchangeResultReactiveHttpInputMessage(ExchangeResult result) { - this.result = result; - } - - @Override - public HttpHeaders getHeaders() { - return this.result.getRequestHeaders(); - } - - @Override - public Flux getBody() { - byte[] requestBodyContent = this.result.getRequestBodyContent(); - DefaultDataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer(requestBodyContent.length); - buffer.write(requestBodyContent); - return Flux.fromArray(new DataBuffer[] { buffer }); - } - - } - -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java deleted file mode 100644 index 5a8416bd4..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverter.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.springframework.http.HttpHeaders; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.OperationResponseFactory; -import org.springframework.restdocs.operation.ResponseConverter; -import org.springframework.restdocs.operation.ResponseCookie; -import org.springframework.test.web.reactive.server.ExchangeResult; -import org.springframework.util.StringUtils; - -/** - * A {@link ResponseConverter} for creating an {@link OperationResponse} derived from an - * {@link ExchangeResult}. - * - * @author Andy Wilkinson - * @author Clyde Stubbs - */ -class WebTestClientResponseConverter implements ResponseConverter { - - @Override - public OperationResponse convert(ExchangeResult result) { - Collection cookies = extractCookies(result); - return new OperationResponseFactory().create(result.getStatus(), extractHeaders(result), - result.getResponseBodyContent(), cookies); - } - - private HttpHeaders extractHeaders(ExchangeResult result) { - HttpHeaders headers = result.getResponseHeaders(); - if (result.getResponseCookies().isEmpty() || headers.containsHeader(HttpHeaders.SET_COOKIE)) { - return headers; - } - result.getResponseCookies() - .values() - .stream() - .flatMap(Collection::stream) - .forEach((cookie) -> headers.add(HttpHeaders.SET_COOKIE, generateSetCookieHeader(cookie))); - return headers; - } - - private String generateSetCookieHeader(org.springframework.http.ResponseCookie cookie) { - StringBuilder header = new StringBuilder(); - header.append(cookie.getName()); - header.append('='); - appendIfAvailable(header, cookie.getValue()); - long maxAge = cookie.getMaxAge().getSeconds(); - if (maxAge > -1) { - header.append("; Max-Age="); - header.append(maxAge); - } - appendIfAvailable(header, "; Domain=", cookie.getDomain()); - appendIfAvailable(header, "; Path=", cookie.getPath()); - if (cookie.isSecure()) { - header.append("; Secure"); - } - if (cookie.isHttpOnly()) { - header.append("; HttpOnly"); - } - return header.toString(); - } - - private Collection extractCookies(ExchangeResult result) { - return result.getResponseCookies() - .values() - .stream() - .flatMap(List::stream) - .map(this::createResponseCookie) - .collect(Collectors.toSet()); - } - - private void appendIfAvailable(StringBuilder header, String value) { - if (StringUtils.hasText(value)) { - header.append(value); - } - } - - private ResponseCookie createResponseCookie(org.springframework.http.ResponseCookie original) { - return new ResponseCookie(original.getName(), original.getValue()); - } - - private void appendIfAvailable(StringBuilder header, String name, String value) { - if (StringUtils.hasText(value)) { - header.append(name); - header.append(value); - } - } - -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentation.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentation.java deleted file mode 100644 index a3f48324b..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentation.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2014-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.util.Map; -import java.util.function.Consumer; - -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.generate.RestDocumentationGenerator; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; -import org.springframework.restdocs.snippet.Snippet; -import org.springframework.test.web.reactive.server.ExchangeResult; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.test.web.reactive.server.WebTestClient.BodyContentSpec; -import org.springframework.test.web.reactive.server.WebTestClient.BodySpec; -import org.springframework.test.web.reactive.server.WebTestClient.Builder; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; - -/** - * Static factory methods for documenting RESTful APIs using WebFlux's - * {@link WebTestClient}. - * - * @author Andy Wilkinson - * @since 2.0.0 - */ -public abstract class WebTestClientRestDocumentation { - - private static final WebTestClientRequestConverter REQUEST_CONVERTER = new WebTestClientRequestConverter(); - - private static final WebTestClientResponseConverter RESPONSE_CONVERTER = new WebTestClientResponseConverter(); - - private WebTestClientRestDocumentation() { - - } - - /** - * Provides access to a {@link ExchangeFilterFunction} that can be used to configure a - * {@link WebTestClient} instance using the given {@code contextProvider}. - * @param contextProvider the context provider - * @return the configurer - * @see Builder#filter(ExchangeFilterFunction) - */ - public static WebTestClientRestDocumentationConfigurer documentationConfiguration( - RestDocumentationContextProvider contextProvider) { - return new WebTestClientRestDocumentationConfigurer(contextProvider); - } - - /** - * Returns a {@link Consumer} that, when called, documents the API call with the given - * {@code identifier} using the given {@code snippets} in addition to any default - * snippets. - * @param identifier an identifier for the API call that is being documented - * @param snippets the snippets - * @param the type of {@link ExchangeResult} that will be consumed - * @return the {@link Consumer} that will document the API call represented by the - * {@link ExchangeResult}. - * @see BodySpec#consumeWith(Consumer) - * @see BodyContentSpec#consumeWith(Consumer) - */ - public static Consumer document(String identifier, Snippet... snippets) { - return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, snippets) - .handle(result, result, retrieveConfiguration(result)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code requestPreprocessor} is applied to the request before it is documented. - * @param identifier an identifier for the API call that is being documented - * @param requestPreprocessor the request preprocessor - * @param snippets the snippets - * @param the type of {@link ExchangeResult} that will be consumed - * @return the {@link Consumer} that will document the API call represented by the - * {@link ExchangeResult}. - */ - public static Consumer document(String identifier, - OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) { - return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, - requestPreprocessor, snippets) - .handle(result, result, retrieveConfiguration(result)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code responsePreprocessor} is applied to the request before it is documented. - * @param identifier an identifier for the API call that is being documented - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - * @param the type of {@link ExchangeResult} that will be consumed - * @return the {@link Consumer} that will document the API call represented by the - * {@link ExchangeResult}. - */ - public static Consumer document(String identifier, - OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) { - return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, - responsePreprocessor, snippets) - .handle(result, result, retrieveConfiguration(result)); - } - - /** - * Documents the API call with the given {@code identifier} using the given - * {@code snippets} in addition to any default snippets. The given - * {@code requestPreprocessor} and {@code responsePreprocessor} are applied to the - * request and response respectively before they are documented. - * @param identifier an identifier for the API call that is being documented - * @param requestPreprocessor the request preprocessor - * @param responsePreprocessor the response preprocessor - * @param snippets the snippets - * @param the type of {@link ExchangeResult} that will be consumed - * @return the {@link Consumer} that will document the API call represented by the - * {@link ExchangeResult}. - */ - public static Consumer document(String identifier, - OperationRequestPreprocessor requestPreprocessor, OperationResponsePreprocessor responsePreprocessor, - Snippet... snippets) { - return (result) -> new RestDocumentationGenerator<>(identifier, REQUEST_CONVERTER, RESPONSE_CONVERTER, - requestPreprocessor, responsePreprocessor, snippets) - .handle(result, result, retrieveConfiguration(result)); - } - - private static Map retrieveConfiguration(ExchangeResult result) { - Map configuration = WebTestClientRestDocumentationConfigurer - .retrieveConfiguration(result.getRequestHeaders()); - configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE, result.getUriTemplate()); - return configuration; - } - -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurer.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurer.java deleted file mode 100644 index fb3f4d507..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurer.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import reactor.core.publisher.Mono; - -import org.springframework.http.HttpHeaders; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.config.RestDocumentationConfigurer; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeFunction; - -/** - * A WebFlux-specific {@link RestDocumentationConfigurer}. - * - * @author Andy Wilkinson - * @since 2.0.0 - */ -public class WebTestClientRestDocumentationConfigurer extends - RestDocumentationConfigurer - implements ExchangeFilterFunction { - - private final WebTestClientSnippetConfigurer snippetConfigurer = new WebTestClientSnippetConfigurer(this); - - private static final Map> configurations = new ConcurrentHashMap<>(); - - private final WebTestClientOperationPreprocessorsConfigurer operationPreprocessorsConfigurer = new WebTestClientOperationPreprocessorsConfigurer( - this); - - private final RestDocumentationContextProvider contextProvider; - - WebTestClientRestDocumentationConfigurer(RestDocumentationContextProvider contextProvider) { - this.contextProvider = contextProvider; - } - - @Override - public WebTestClientSnippetConfigurer snippets() { - return this.snippetConfigurer; - } - - @Override - public WebTestClientOperationPreprocessorsConfigurer operationPreprocessors() { - return this.operationPreprocessorsConfigurer; - } - - private Map createConfiguration() { - RestDocumentationContext context = this.contextProvider.beforeOperation(); - Map configuration = new HashMap<>(); - configuration.put(RestDocumentationContext.class.getName(), context); - apply(configuration, context); - return configuration; - } - - static Map retrieveConfiguration(HttpHeaders headers) { - String requestId = headers.getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID); - Map configuration = configurations.remove(requestId); - Assert.state(configuration != null, () -> "REST Docs configuration not found. Did you forget to register a " - + WebTestClientRestDocumentationConfigurer.class.getSimpleName() + " as a filter?"); - return configuration; - } - - @Override - public Mono filter(ClientRequest request, ExchangeFunction next) { - String index = request.headers().getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID); - configurations.put(index, createConfiguration()); - return next.exchange(applyUriDefaults(request)); - } - - private ClientRequest applyUriDefaults(ClientRequest request) { - URI requestUri = request.url(); - if (StringUtils.hasLength(requestUri.getHost())) { - return request; - } - try { - requestUri = new URI("http", requestUri.getUserInfo(), "localhost", 8080, requestUri.getPath(), - requestUri.getQuery(), requestUri.getFragment()); - return ClientRequest.from(request).url(requestUri).build(); - } - catch (URISyntaxException ex) { - throw new IllegalStateException(ex); - } - } - -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientSnippetConfigurer.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientSnippetConfigurer.java deleted file mode 100644 index 68c0916c6..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/WebTestClientSnippetConfigurer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import reactor.core.publisher.Mono; - -import org.springframework.restdocs.config.SnippetConfigurer; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeFunction; - -/** - * A {@link SnippetConfigurer} for WebFlux that can be used to configure the generated - * documentation snippets. - * - * @author Andy Wilkinson - * @since 2.0.0 - */ -public class WebTestClientSnippetConfigurer - extends SnippetConfigurer - implements ExchangeFilterFunction { - - WebTestClientSnippetConfigurer(WebTestClientRestDocumentationConfigurer parent) { - super(parent); - } - - @Override - public Mono filter(ClientRequest request, ExchangeFunction next) { - return and().filter(request, next); - } - -} diff --git a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/package-info.java b/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/package-info.java deleted file mode 100644 index 9a49f263e..000000000 --- a/spring-restdocs-webtestclient/src/main/java/org/springframework/restdocs/webtestclient/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Core classes for using Spring REST Docs with Spring Framework's WebTestClient. - */ -package org.springframework.restdocs.webtestclient; diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java deleted file mode 100644 index 1087ea411..000000000 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRequestConverterTests.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import org.springframework.core.io.ByteArrayResource; -import org.springframework.http.ContentDisposition; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationRequest; -import org.springframework.restdocs.operation.OperationRequestPart; -import org.springframework.test.web.reactive.server.ExchangeResult; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyExtractors; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -import static org.springframework.web.reactive.function.server.RequestPredicates.POST; - -/** - * Tests for {@link WebTestClientRequestConverter}. - * - * @author Andy Wilkinson - */ -class WebTestClientRequestConverterTests { - - private final WebTestClientRequestConverter converter = new WebTestClientRequestConverter(); - - @Test - void httpRequest() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void httpRequestWithCustomPort() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) - .configureClient() - .baseUrl("http://localhost:8080") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost:8080/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void requestWithHeaders() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/"), (req) -> null)) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo") - .header("a", "alpha", "apple") - .header("b", "bravo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - assertThat(request.getHeaders().headerSet()).contains(entry("a", Arrays.asList("alpha", "apple")), - entry("b", Arrays.asList("bravo"))); - } - - @Test - void httpsRequest() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) - .configureClient() - .baseUrl("https://localhost") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("https://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void httpsRequestWithCustomPort() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) - .configureClient() - .baseUrl("https://localhost:8443") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("https://localhost:8443/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void getRequestWithQueryString() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo?a=alpha&b=bravo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&b=bravo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - } - - @Test - void postRequestWithFormDataParameters() { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.addAll("a", Arrays.asList("alpha", "apple")); - parameters.addAll("b", Arrays.asList("br&vo")); - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> { - req.body(BodyExtractors.toFormData()).block(); - return null; - })) - .configureClient() - .baseUrl("http://localhost") - .build() - .post() - .uri("/foo") - .body(BodyInserters.fromFormData(parameters)) - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getContentAsString()).isEqualTo("a=alpha&a=apple&b=br%26vo"); - assertThat(request.getHeaders().getContentType()).satisfiesAnyOf( - (mediaType) -> assertThat(mediaType) - .isEqualTo(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, StandardCharsets.UTF_8)), - (mediaType) -> assertThat(mediaType).isEqualTo(new MediaType(MediaType.APPLICATION_FORM_URLENCODED))); - } - - @Test - void postRequestWithQueryStringParameters() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> { - req.body(BodyExtractors.toFormData()).block(); - return null; - })) - .configureClient() - .baseUrl("http://localhost") - .build() - .post() - .uri(URI.create("http://localhost/foo?a=alpha&a=apple&b=br%26vo")) - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&a=apple&b=br%26vo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - } - - @Test - void postRequestWithQueryStringAndFormDataParameters() { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.addAll("a", Arrays.asList("apple")); - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> { - req.body(BodyExtractors.toFormData()).block(); - return null; - })) - .configureClient() - .baseUrl("http://localhost") - .build() - .post() - .uri(URI.create("http://localhost/foo?a=alpha&b=br%26vo")) - .body(BodyInserters.fromFormData(parameters)) - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo?a=alpha&b=br%26vo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getContentAsString()).isEqualTo("a=apple"); - assertThat(request.getHeaders().getContentType()).satisfiesAnyOf( - (mediaType) -> assertThat(mediaType) - .isEqualTo(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, StandardCharsets.UTF_8)), - (mediaType) -> assertThat(mediaType).isEqualTo(new MediaType(MediaType.APPLICATION_FORM_URLENCODED))); - } - - @Test - void postRequestWithNoContentType() { - ExchangeResult result = WebTestClient - .bindToRouterFunction(RouterFunctions.route(POST("/foo"), (req) -> ServerResponse.ok().build())) - .configureClient() - .baseUrl("http://localhost") - .build() - .post() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - } - - @Test - void multipartUpload() { - MultiValueMap multipartData = new LinkedMultiValueMap<>(); - multipartData.add("file", new byte[] { 1, 2, 3, 4 }); - ExchangeResult result = WebTestClient - .bindToRouterFunction(RouterFunctions.route(POST("/foo"), - (req) -> ServerResponse.ok() - .body(req.body(BodyExtractors.toMultipartData()).map((parts) -> parts.size()), Integer.class))) - .configureClient() - .baseUrl("http://localhost") - .build() - .post() - .uri("/foo") - .body(BodyInserters.fromMultipartData(multipartData)) - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getParts()).hasSize(1); - OperationRequestPart part = request.getParts().iterator().next(); - assertThat(part.getName()).isEqualTo("file"); - assertThat(part.getSubmittedFileName()).isNull(); - assertThat(part.getHeaders().size()).isEqualTo(2); - assertThat(part.getHeaders().getContentLength()).isEqualTo(4L); - assertThat(part.getHeaders().getContentDisposition().getName()).isEqualTo("file"); - assertThat(part.getContent()).containsExactly(1, 2, 3, 4); - } - - @Test - void multipartUploadFromResource() { - MultiValueMap multipartData = new LinkedMultiValueMap<>(); - multipartData.add("file", new ByteArrayResource(new byte[] { 1, 2, 3, 4 }) { - - @Override - public String getFilename() { - return "image.png"; - } - - }); - ExchangeResult result = WebTestClient - .bindToRouterFunction(RouterFunctions.route(POST("/foo"), - (req) -> ServerResponse.ok() - .body(req.body(BodyExtractors.toMultipartData()).map((parts) -> parts.size()), Integer.class))) - .configureClient() - .baseUrl("http://localhost") - .build() - .post() - .uri("/foo") - .body(BodyInserters.fromMultipartData(multipartData)) - .exchange() - .expectBody() - .returnResult(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.POST); - assertThat(request.getParts()).hasSize(1); - OperationRequestPart part = request.getParts().iterator().next(); - assertThat(part.getName()).isEqualTo("file"); - assertThat(part.getSubmittedFileName()).isEqualTo("image.png"); - assertThat(part.getHeaders().size()).isEqualTo(3); - assertThat(part.getHeaders().getContentLength()).isEqualTo(4); - ContentDisposition contentDisposition = part.getHeaders().getContentDisposition(); - assertThat(contentDisposition.getName()).isEqualTo("file"); - assertThat(contentDisposition.getFilename()).isEqualTo("image.png"); - assertThat(part.getHeaders().getContentType()).isEqualTo(MediaType.IMAGE_PNG); - assertThat(part.getContent()).containsExactly(1, 2, 3, 4); - } - - @Test - void requestWithCookies() { - ExchangeResult result = WebTestClient.bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> null)) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo") - .cookie("cookieName1", "cookieVal1") - .cookie("cookieName2", "cookieVal2") - .exchange() - .expectBody() - .returnResult(); - assertThat(result.getRequestHeaders().get(HttpHeaders.COOKIE)).isNotNull(); - OperationRequest request = this.converter.convert(result); - assertThat(request.getUri()).isEqualTo(URI.create("http://localhost/foo")); - assertThat(request.getMethod()).isEqualTo(HttpMethod.GET); - assertThat(request.getCookies()).hasSize(2); - assertThat(request.getHeaders().get(HttpHeaders.COOKIE)).isNull(); - assertThat(request.getCookies()).extracting("name").containsExactly("cookieName1", "cookieName2"); - assertThat(request.getCookies()).extracting("value").containsExactly("cookieVal1", "cookieVal2"); - } - -} diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java deleted file mode 100644 index e50fc5a5d..000000000 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientResponseConverterTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; -import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.ResponseCookie; -import org.springframework.test.web.reactive.server.ExchangeResult; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.springframework.web.reactive.function.server.RequestPredicates.GET; - -/** - * Tests for {@link WebTestClientResponseConverter}. - * - * @author Andy Wilkinson - */ -class WebTestClientResponseConverterTests { - - private final WebTestClientResponseConverter converter = new WebTestClientResponseConverter(); - - @Test - void basicResponse() { - ExchangeResult result = WebTestClient - .bindToRouterFunction( - RouterFunctions.route(GET("/foo"), (req) -> ServerResponse.ok().bodyValue("Hello, World!"))) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationResponse response = this.converter.convert(result); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK); - assertThat(response.getContentAsString()).isEqualTo("Hello, World!"); - assertThat(response.getHeaders().getContentType()) - .isEqualTo(MediaType.parseMediaType("text/plain;charset=UTF-8")); - assertThat(response.getHeaders().getContentLength()).isEqualTo(13); - } - - @Test - void responseWithCookie() { - ExchangeResult result = WebTestClient - .bindToRouterFunction(RouterFunctions.route(GET("/foo"), - (req) -> ServerResponse.ok() - .cookie(org.springframework.http.ResponseCookie.from("name", "value") - .domain("localhost") - .httpOnly(true) - .build()) - .build())) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationResponse response = this.converter.convert(result); - assertThat(response.getHeaders().headerSet()).containsOnly( - entry(HttpHeaders.SET_COOKIE, Collections.singletonList("name=value; Domain=localhost; HttpOnly"))); - assertThat(response.getCookies()).hasSize(1); - assertThat(response.getCookies()).first().extracting(ResponseCookie::getName).isEqualTo("name"); - assertThat(response.getCookies()).first().extracting(ResponseCookie::getValue).isEqualTo("value"); - } - - @Test - void responseWithNonStandardStatusCode() { - ExchangeResult result = WebTestClient - .bindToRouterFunction(RouterFunctions.route(GET("/foo"), (req) -> ServerResponse.status(210).build())) - .configureClient() - .baseUrl("http://localhost") - .build() - .get() - .uri("/foo") - .exchange() - .expectBody() - .returnResult(); - OperationResponse response = this.converter.convert(result); - assertThat(response.getStatus()).isEqualTo(HttpStatusCode.valueOf(210)); - } - -} diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java deleted file mode 100644 index efa22ebfb..000000000 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationConfigurerTests.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.net.URI; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; - -import org.springframework.http.HttpMethod; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ExchangeFunction; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link WebTestClientRestDocumentationConfigurer}. - * - * @author Andy Wilkinson - */ -@ExtendWith(RestDocumentationExtension.class) -class WebTestClientRestDocumentationConfigurerTests { - - private WebTestClientRestDocumentationConfigurer configurer; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - this.configurer = new WebTestClientRestDocumentationConfigurer(restDocumentation); - - } - - @Test - void configurationCanBeRetrievedButOnlyOnce() { - ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("/test")) - .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1") - .build(); - this.configurer.filter(request, mock(ExchangeFunction.class)); - assertThat(WebTestClientRestDocumentationConfigurer.retrieveConfiguration(request.headers())).isNotNull(); - assertThatIllegalStateException() - .isThrownBy(() -> WebTestClientRestDocumentationConfigurer.retrieveConfiguration(request.headers())); - } - - @Test - void requestUriHasDefaultsAppliedWhenItHasNoHost() { - ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("/test?foo=bar#baz")) - .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1") - .build(); - ExchangeFunction exchangeFunction = mock(ExchangeFunction.class); - this.configurer.filter(request, exchangeFunction); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ClientRequest.class); - verify(exchangeFunction).exchange(requestCaptor.capture()); - assertThat(requestCaptor.getValue().url()).isEqualTo(URI.create("http://localhost:8080/test?foo=bar#baz")); - } - - @Test - void requestUriIsNotChangedWhenItHasAHost() { - ClientRequest request = ClientRequest - .create(HttpMethod.GET, URI.create("https://api.example.com:4567/test?foo=bar#baz")) - .header(WebTestClient.WEBTESTCLIENT_REQUEST_ID, "1") - .build(); - ExchangeFunction exchangeFunction = mock(ExchangeFunction.class); - this.configurer.filter(request, exchangeFunction); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ClientRequest.class); - verify(exchangeFunction).exchange(requestCaptor.capture()); - assertThat(requestCaptor.getValue().url()) - .isEqualTo(URI.create("https://api.example.com:4567/test?foo=bar#baz")); - } - -} diff --git a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java b/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java deleted file mode 100644 index 30db5c4a6..000000000 --- a/spring-restdocs-webtestclient/src/test/java/org/springframework/restdocs/webtestclient/WebTestClientRestDocumentationIntegrationTests.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2014-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.webtestclient; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.assertj.core.api.Condition; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseCookie; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.restdocs.templates.TemplateFormat; -import org.springframework.restdocs.templates.TemplateFormats; -import org.springframework.restdocs.testfixtures.SnippetConditions; -import org.springframework.restdocs.testfixtures.SnippetConditions.CodeBlockCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.HttpResponseCondition; -import org.springframework.restdocs.testfixtures.SnippetConditions.TableCondition; -import org.springframework.test.web.reactive.server.EntityExchangeResult; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.util.FileCopyUtils; -import org.springframework.util.FileSystemUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.reactive.function.BodyExtractors; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.fail; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.partWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; -import static org.springframework.restdocs.request.RequestDocumentation.requestParts; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; -import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration; -import static org.springframework.web.reactive.function.BodyInserters.fromValue; - -/** - * Integration tests for using Spring REST Docs with Spring Framework's WebTestClient. - * - * @author Andy Wilkinson - */ -@ExtendWith(RestDocumentationExtension.class) -public class WebTestClientRestDocumentationIntegrationTests { - - private WebTestClient webTestClient; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - RouterFunction route = RouterFunctions - .route(RequestPredicates.GET("/"), - (request) -> ServerResponse.status(HttpStatus.OK).body(fromValue(new Person("Jane", "Doe")))) - .andRoute(RequestPredicates.GET("/{foo}/{bar}"), - (request) -> ServerResponse.status(HttpStatus.OK).body(fromValue(new Person("Jane", "Doe")))) - .andRoute(RequestPredicates.POST("/upload"), - (request) -> request.body(BodyExtractors.toMultipartData()) - .map((parts) -> ServerResponse.status(HttpStatus.OK).build().block())) - .andRoute(RequestPredicates.GET("/set-cookie"), - (request) -> ServerResponse.ok() - .cookie(ResponseCookie.from("name", "value").domain("localhost").httpOnly(true).build()) - .build()); - this.webTestClient = WebTestClient.bindToRouterFunction(route) - .configureClient() - .baseUrl("https://api.example.com") - .filter(documentationConfiguration(restDocumentation)) - .build(); - } - - @Test - void defaultSnippetGeneration() { - File outputDir = new File("build/generated-snippets/default-snippets"); - FileSystemUtils.deleteRecursively(outputDir); - this.webTestClient.get() - .uri("/") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("default-snippets")); - assertExpectedSnippetFilesExist(outputDir, "http-request.adoc", "http-response.adoc", "curl-request.adoc", - "httpie-request.adoc", "request-body.adoc", "response-body.adoc"); - } - - @Test - void pathParametersSnippet() { - this.webTestClient.get() - .uri("/{foo}/{bar}", "1", "2") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith( - document("path-parameters", pathParameters(parameterWithName("foo").description("Foo description"), - parameterWithName("bar").description("Bar description")))); - assertThat(new File("build/generated-snippets/path-parameters/path-parameters.adoc")).has(content( - tableWithTitleAndHeader(TemplateFormats.asciidoctor(), "+/{foo}/{bar}+", "Parameter", "Description") - .row("`foo`", "Foo description") - .row("`bar`", "Bar description"))); - } - - @Test - void queryParametersSnippet() { - this.webTestClient.get() - .uri("/?a=alpha&b=bravo") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("query-parameters", - queryParameters(parameterWithName("a").description("Alpha description"), - parameterWithName("b").description("Bravo description")))); - assertThat(new File("build/generated-snippets/query-parameters/query-parameters.adoc")) - .has(content(tableWithHeader(TemplateFormats.asciidoctor(), "Parameter", "Description") - .row("`a`", "Alpha description") - .row("`b`", "Bravo description"))); - } - - @Test - void multipart() { - MultiValueMap multipartData = new LinkedMultiValueMap<>(); - multipartData.add("a", "alpha"); - multipartData.add("b", "bravo"); - Consumer> documentation = document("multipart", - requestParts(partWithName("a").description("Part a"), partWithName("b").description("Part b"))); - this.webTestClient.post() - .uri("/upload") - .body(BodyInserters.fromMultipartData(multipartData)) - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(documentation); - assertThat(new File("build/generated-snippets/multipart/request-parts.adoc")) - .has(content(tableWithHeader(TemplateFormats.asciidoctor(), "Part", "Description").row("`a`", "Part a") - .row("`b`", "Part b"))); - } - - @Test - void responseWithSetCookie() { - this.webTestClient.get() - .uri("/set-cookie") - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("set-cookie")); - assertThat(new File("build/generated-snippets/set-cookie/http-response.adoc")) - .has(content(httpResponse(TemplateFormats.asciidoctor(), HttpStatus.OK).header(HttpHeaders.SET_COOKIE, - "name=value; Domain=localhost; HttpOnly"))); - } - - @Test - void curlSnippetWithCookies() { - this.webTestClient.get() - .uri("/") - .cookie("cookieName", "cookieVal") - .accept(MediaType.APPLICATION_JSON) - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("curl-snippet-with-cookies")); - assertThat(new File("build/generated-snippets/curl-snippet-with-cookies/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ curl 'https://api.example.com/' -i -X GET \\%n" - + " -H 'Accept: application/json' \\%n" + " --cookie 'cookieName=cookieVal'")))); - } - - @Test - void curlSnippetWithEmptyParameterQueryString() { - this.webTestClient.get() - .uri("/?a=") - .accept(MediaType.APPLICATION_JSON) - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("curl-snippet-with-empty-parameter-query-string")); - assertThat( - new File("build/generated-snippets/curl-snippet-with-empty-parameter-query-string/curl-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash").withContent(String - .format("$ curl 'https://api.example.com/?a=' -i -X GET \\%n" + " -H 'Accept: application/json'")))); - } - - @Test - void httpieSnippetWithCookies() { - this.webTestClient.get() - .uri("/") - .cookie("cookieName", "cookieVal") - .accept(MediaType.APPLICATION_JSON) - .exchange() - .expectStatus() - .isOk() - .expectBody() - .consumeWith(document("httpie-snippet-with-cookies")); - assertThat(new File("build/generated-snippets/httpie-snippet-with-cookies/httpie-request.adoc")) - .has(content(codeBlock(TemplateFormats.asciidoctor(), "bash") - .withContent(String.format("$ http GET 'https://api.example.com/' \\%n" - + " 'Accept:application/json' \\%n" + " 'Cookie:cookieName=cookieVal'")))); - } - - @Test - void illegalStateExceptionShouldBeThrownWhenCallDocumentWebClientNotConfigured() { - assertThatThrownBy(() -> this.webTestClient - .mutateWith((builder, httpHandlerBuilder, connector) -> builder.filters(List::clear).build()) - .get() - .uri("/") - .exchange() - .expectBody() - .consumeWith(document("default-snippets"))).isInstanceOf(IllegalStateException.class) - .hasMessage("REST Docs configuration not found. Did you forget to register a " - + "WebTestClientRestDocumentationConfigurer as a filter?"); - } - - private void assertExpectedSnippetFilesExist(File directory, String... snippets) { - Set actual = new HashSet<>(Arrays.asList(directory.listFiles())); - Set expected = Stream.of(snippets) - .map((snippet) -> new File(directory, snippet)) - .collect(Collectors.toSet()); - assertThat(actual).isEqualTo(expected); - } - - private Condition content(final Condition delegate) { - return new Condition<>() { - - @Override - public boolean matches(File value) { - try { - return delegate.matches(FileCopyUtils - .copyToString(new InputStreamReader(new FileInputStream(value), StandardCharsets.UTF_8))); - } - catch (IOException ex) { - fail("Failed to read '" + value + "'", ex); - return false; - } - } - - }; - } - - private CodeBlockCondition codeBlock(TemplateFormat format, String language) { - return SnippetConditions.codeBlock(format, language); - } - - private HttpResponseCondition httpResponse(TemplateFormat format, HttpStatus status) { - return SnippetConditions.httpResponse(format, status); - } - - private TableCondition tableWithHeader(TemplateFormat format, String... headers) { - return SnippetConditions.tableWithHeader(format, headers); - } - - private TableCondition tableWithTitleAndHeader(TemplateFormat format, String title, String... headers) { - return SnippetConditions.tableWithTitleAndHeader(format, title, headers); - } - - /** - * A person. - */ - public static class Person { - - private final String firstName; - - private final String lastName; - - Person(String firstName, String lastName) { - this.firstName = firstName; - this.lastName = lastName; - } - - public String getFirstName() { - return this.firstName; - } - - public String getLastName() { - return this.lastName; - } - - } - -}