├── .gitattributes ├── .github └── workflows │ ├── options-tests-with-docker-build.yml │ ├── purge-images.yml │ ├── push-dockerhub.yml │ ├── push-github.yml │ ├── rest-api-tests-with-docker-image.yml │ ├── test-dkr-build.yml │ ├── test-dkr-image.yml │ ├── test-pr.yml │ └── update-arlington-workflow.yml ├── .gitignore ├── Dockerfile ├── Dockerfile_dev ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── charts └── verapdf-rest │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── hpa.yaml │ ├── ingress.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── tests │ │ └── test-connection.yaml │ └── values.yaml ├── config ├── app.xml ├── features.xml ├── fixer.xml ├── plugins.xml └── validator.xml ├── kubernetes.yaml ├── pom.xml ├── requirements.txt ├── server.yml ├── settings.xml ├── src └── main │ ├── java │ └── org │ │ ├── openpreservation │ │ └── bytestreams │ │ │ ├── ByteStreamId.java │ │ │ ├── ByteStreamIdImpl.java │ │ │ ├── ByteStreams.java │ │ │ └── package-info.java │ │ └── verapdf │ │ └── rest │ │ ├── app │ │ ├── VeraPdfRestApplication.java │ │ └── VeraPdfRestConfiguration.java │ │ ├── environment │ │ ├── Environment.java │ │ ├── Environments.java │ │ ├── JvmDetails.java │ │ ├── OsDetails.java │ │ └── ServerDetails.java │ │ ├── package-info.java │ │ ├── resources │ │ ├── ApiResource.java │ │ ├── ByteStreamResource.java │ │ ├── HomePageResource.java │ │ ├── ProfileResource.java │ │ ├── ValidateResource.java │ │ └── ValidationExceptionMapper.java │ │ └── views │ │ └── RestClientView.java │ └── resources │ ├── assets │ ├── css │ │ └── verapdf.css │ ├── img │ │ ├── vera-pages.jpg │ │ └── veraPDF-logo-400.png │ └── js │ │ ├── docs.min.js │ │ ├── jquery.bootstrap.wizard.js │ │ ├── mustache.js │ │ ├── sha1.js │ │ ├── vera.results.js │ │ └── verapdf.js │ └── org │ └── verapdf │ ├── release │ └── rest.properties │ └── rest │ └── views │ └── restclient.mustache └── tests ├── Options ├── config │ ├── app │ │ ├── format.bats │ │ ├── format │ │ │ └── app.xml │ │ ├── type.bats │ │ ├── type │ │ │ ├── app.xml │ │ │ ├── features.xml │ │ │ └── validator.xml │ │ ├── wikiPath.bats │ │ └── wikiPath │ │ │ └── app.xml │ ├── features │ │ ├── features.bats │ │ └── features │ │ │ ├── app.xml │ │ │ └── features.xml │ └── validator │ │ ├── debug.bats │ │ ├── debug │ │ └── validator.xml │ │ ├── flavour.bats │ │ ├── flavour │ │ └── validator.xml │ │ ├── isLogsEnabled.bats │ │ ├── isLogsEnabled │ │ └── validator.xml │ │ ├── loggingLevel.bats │ │ ├── loggingLevel │ │ └── validator.xml │ │ ├── maxFails.bats │ │ ├── maxFails │ │ └── validator.xml │ │ ├── maxNumberOfDisplayedFailedChecks.bats │ │ ├── maxNumberOfDisplayedFailedChecks │ │ ├── validator_Checks_0.xml │ │ └── validator_Checks_2.xml │ │ ├── recordPasses.bats │ │ ├── recordPasses │ │ └── validator.xml │ │ ├── showErrorMessages.bats │ │ └── showErrorMessages │ │ └── validator.xml ├── services │ ├── bindMountLocalFiles │ │ ├── steps_in_docker.sh │ │ └── withBindMountLocalFiles.bats │ └── limitingFileSize │ │ └── limitingFileSize.bats └── setup_suite.bash ├── Resources ├── 5-t02-fail-a.pdf ├── 6.1.3-01-fail-5.pdf ├── Mustang_505.pdf ├── PDFBOX_allowed_file_size.pdf ├── a.pdf ├── a_for_debug.pdf ├── orange.pdf ├── veraPDFPDFAConformanceCheckerGUI.pdf └── veraPDF_MF3.pdf ├── __init__.py ├── conftest.py ├── rest_api ├── __init__.py ├── model │ ├── __init__.py │ ├── api_info │ │ ├── __init__.py │ │ ├── api_endpoint.py │ │ ├── environment.py │ │ ├── java.py │ │ ├── os.py │ │ └── server.py │ ├── api_profiles │ │ ├── __init__.py │ │ ├── profile.py │ │ ├── profile_id.py │ │ ├── profile_ids.py │ │ ├── profile_rule.py │ │ ├── profile_variables.py │ │ ├── profiles_flavours.py │ │ └── rule_id.py │ ├── api_sha1 │ │ ├── __init__.py │ │ └── sha1.py │ └── api_validate_details │ │ ├── __init__.py │ │ ├── api_validate_details.py │ │ ├── job.py │ │ ├── log.py │ │ ├── report.py │ │ ├── result.py │ │ └── summary.py └── tests │ ├── __init__.py │ ├── api_info │ ├── __init__.py │ ├── test_api.py │ └── test_api_info.py │ ├── api_profiles │ ├── __init__.py │ ├── test_api_profiles.py │ ├── test_api_profiles_clause.py │ ├── test_api_profiles_flavours.py │ ├── test_api_profiles_id.py │ ├── test_api_profiles_ids.py │ └── test_api_profiles_ruleids.py │ ├── api_sha1 │ ├── __init__.py │ ├── api_sha1.py │ └── test_api_sha1_null.py │ ├── api_validate │ ├── __init__.py │ ├── test_api_validate_details.py │ ├── test_api_validate_profileId.py │ ├── test_api_validate_sha_profileId.py │ └── test_validate_pdf_by_url.py │ └── base_test.py └── tools └── test_helper └── common-setup.bash /.gitattributes: -------------------------------------------------------------------------------- 1 | # Handle line endings automatically for files detected as text 2 | # and leave all files detected as binary untouched. 3 | * text=auto 4 | 5 | # 6 | # The above will handle all files NOT found below 7 | # 8 | # These files are text and should be normalized (Convert crlf => lf) 9 | *.css text 10 | *.df text 11 | *.htm text 12 | *.html text 13 | *.java text 14 | *.js text 15 | *.json text 16 | *.jsp text 17 | *.jspf text 18 | *.properties text 19 | *.sh text 20 | *.tld text 21 | *.txt text 22 | *.xml text 23 | 24 | # These files are binary and should be left untouched 25 | # (binary is a macro for -text -diff) 26 | *.class binary 27 | *.dll binary 28 | *.ear binary 29 | *.gif binary 30 | *.ico binary 31 | *.jar binary 32 | *.jpg binary 33 | *.jpeg binary 34 | *.png binary 35 | *.so binary 36 | *.war binary 37 | 38 | -------------------------------------------------------------------------------- /.github/workflows/options-tests-with-docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Options tests with docker build 2 | 3 | on: 4 | schedule: 5 | - cron: "05 09 * * 1" 6 | push: 7 | branches: 8 | - 'integration' 9 | 10 | jobs: 11 | build: 12 | if: github.repository == 'veraPDF/veraPDF-rest' 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout the branch 17 | uses: actions/checkout@v3 18 | 19 | - name: Build and run Docker Image 20 | run: | 21 | docker build --build-arg GH_CHECKOUT=integration -t rest:dev . && docker run --user root -d -p 8080:8080 -p 8081:8081 -e VERAPDF_MAX_FILE_SIZE=1 -v /tmp:/home/Res_tmp rest:dev 22 | echo whoami: $(docker inspect $(docker ps -q) --format '{{.Config.User}} {{.Name}}') 23 | 24 | - name: Check Docker Container 25 | run: | 26 | docker images 27 | docker ps 28 | 29 | - name: Preparing Linux packages(Bats-core and more ...) 30 | if: runner.os == 'Linux' 31 | working-directory: ./tests 32 | run: | 33 | echo whoami: $(whoami) 34 | mkdir ./results 35 | 36 | sudo apt-get update -y 37 | sudo apt install git -y 38 | 39 | sudo apt-get install python3 -y 40 | sudo apt-get install python3-pip -y 41 | 42 | sudo pip install junit2html 43 | 44 | echo $(pip list) 45 | 46 | git --version 47 | 48 | echo "Adding: bats-core.git, bats-support.git and bats-assert.git" 49 | git clone https://github.com/bats-core/bats-core.git ./bats 50 | git clone https://github.com/bats-core/bats-support.git ./bats-support 51 | git clone https://github.com/bats-core/bats-assert.git ./bats-assert 52 | 53 | ./bats/bin/bats -v 54 | 55 | - name: Running tests ... Options 56 | working-directory: ./tests 57 | run: | 58 | echo pwd: $PWD 59 | echo dir: $(ls ./) 60 | ./bats/bin/bats -r ./Options --show-output-of-passing-tests --print-output-on-failure --report-formatter junit --output ./results 61 | 62 | - name: Generating report 63 | if: always() 64 | working-directory: ./tests/results 65 | run: | 66 | echo pwd: $PWD 67 | junit2html report.xml --report-matrix report.html 68 | 69 | - name: Uploading report 70 | uses: actions/upload-artifact@v4 71 | if: success() || failure() 72 | with: 73 | name: Results 74 | path: "./tests/results" 75 | 76 | - name: Notify slack tests succeeded 77 | if: success() 78 | env: 79 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} 80 | uses: voxmedia/github-action-slack-notify-build@v1 81 | with: 82 | channel_id: C03E3JJGLQL 83 | status: SUCCESS 84 | color: good 85 | 86 | - name: Notify slack tests failed 87 | if: failure() 88 | env: 89 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} 90 | uses: voxmedia/github-action-slack-notify-build@v1 91 | with: 92 | channel_id: C03E3JJGLQL 93 | status: FAILED 94 | color: danger 95 | -------------------------------------------------------------------------------- /.github/workflows/purge-images.yml: -------------------------------------------------------------------------------- 1 | name: Purge GitHub container registry. 2 | 3 | on: 4 | workflow_dispatch: 5 | # Runs daily at midnight 6 | schedule: 7 | - cron: '0 0 * * *' 8 | 9 | jobs: 10 | purge-images: 11 | if: github.repository == 'veraPDF/veraPDF-rest' 12 | name: Delete old untagged images from ghcr.io 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Delete all PR containers older than a week 16 | uses: snok/container-retention-policy@v2 17 | with: 18 | image-names: rest, arlington 19 | cut-off: 1 week ago UTC 20 | account-type: org 21 | org-name: veraPDF 22 | keep-at-least: 1 23 | skip-tags: latest 24 | untagged-only: false 25 | token: ${{ secrets.PAT }} 26 | -------------------------------------------------------------------------------- /.github/workflows/push-dockerhub.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docker image 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | 8 | jobs: 9 | push_to_registry: 10 | if: github.repository == 'veraPDF/veraPDF-rest' 11 | name: Push Docker image to Docker Hub 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out the repo 15 | uses: actions/checkout@v3 16 | 17 | - name: Log in to Docker Hub 18 | uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a 19 | with: 20 | username: ${{ secrets.DOCKER_USERNAME }} 21 | password: ${{ secrets.DOCKER_PASSWORD }} 22 | 23 | - name: Extract metadata (tags, labels) for Docker 24 | id: meta 25 | uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 26 | with: 27 | images: verapdf/rest 28 | 29 | - name: Build and push Docker image 30 | uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 31 | with: 32 | context: . 33 | file: ./Dockerfile 34 | push: true 35 | tags: verapdf/rest:latest,verapdf/rest:v1.28.1 36 | labels: ${{ steps.meta.outputs.labels }} 37 | -------------------------------------------------------------------------------- /.github/workflows/push-github.yml: -------------------------------------------------------------------------------- 1 | name: Publish image to GitHub Packages 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - 'integration' 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: veraPDF/rest 11 | 12 | jobs: 13 | publish: 14 | if: github.repository == 'veraPDF/veraPDF-rest' 15 | name: Build and push Docker image to GitHub packages 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - name: Log in to the Container registry 22 | uses: docker/login-action@v2 23 | with: 24 | registry: ${{ env.REGISTRY }} 25 | username: ${{ github.actor }} 26 | password: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | - name: Extract metadata (tags, labels) for GitHub 29 | id: meta 30 | uses: docker/metadata-action@v2 31 | with: 32 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 33 | 34 | - name: Build and push Docker image 35 | uses: docker/build-push-action@v2 36 | with: 37 | context: . 38 | push: true 39 | file: Dockerfile 40 | build-args: GH_CHECKOUT=integration 41 | tags: ghcr.io/verapdf/rest:latest 42 | labels: ${{ steps.meta.outputs.labels }} 43 | 44 | test: 45 | name: Test Docker image 46 | needs: [ publish ] 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v3 50 | 51 | - name: Set up JDK 1.11 52 | uses: actions/setup-java@v1 53 | with: 54 | java-version: 1.11 55 | 56 | - name: Run rest-api-tests-with-docker-image 57 | uses: actions/github-script@v6 58 | with: 59 | github-token: ${{ secrets.WORKFLOW_TOKEN }} 60 | script: | 61 | await github.rest.actions.createWorkflowDispatch({ 62 | owner: 'veraPDF', 63 | repo: 'veraPDF-rest', 64 | workflow_id: 'rest-api-tests-with-docker-image.yml', 65 | ref: 'integration' 66 | }) 67 | 68 | 69 | -------------------------------------------------------------------------------- /.github/workflows/rest-api-tests-with-docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Rest-api tests - ghcr.io/verapdf/rest:latest 2 | 3 | on: 4 | schedule: 5 | - cron: "05 09 * * 1" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | test: 10 | if: github.repository == 'veraPDF/veraPDF-rest' 11 | name: Rest-api tests running in Docker container 12 | runs-on: ubuntu-latest 13 | container: ubuntu 14 | services: 15 | verarest: 16 | image: ghcr.io/verapdf/rest:latest 17 | ports: 18 | - 8080:8080 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Preparing Linux packages(Python and more ...) 23 | if: runner.os == 'Linux' 24 | run: | 25 | echo pwd: $PWD 26 | echo dir: $(ls ./) 27 | 28 | apt-get update -y 29 | apt-get install python3 -y 30 | apt-get install python3-pip -y 31 | echo install python3-venv -y 32 | apt-get install python3-venv -y 33 | 34 | echo python3 -m venv .venv 35 | python3 -m venv .venv 36 | echo . .venv/bin/activate 37 | . .venv/bin/activate 38 | echo which python 39 | which python 40 | echo python3 -m pip install -r requirements.txt 41 | python3 -m pip install -r requirements.txt 42 | echo $(pip list) 43 | echo pwd: $PWD 44 | mkdir ./results 45 | echo $(pip list) 46 | echo dir: $(ls ./) 47 | echo $(which python) 48 | echo $(pytest --version) 49 | pytest --base_url=http://verarest:8080 --html=./results/report.html ./tests/ 50 | echo $(ls ./results/) 51 | 52 | - name: Generating report 53 | uses: actions/upload-artifact@v4 54 | if: success() || failure() 55 | with: 56 | name: Results 57 | path: "./results" 58 | 59 | - name: Notify slack tests succeeded 60 | if: success() 61 | env: 62 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} 63 | uses: voxmedia/github-action-slack-notify-build@v1 64 | with: 65 | channel_id: C03E3JJGLQL 66 | status: SUCCESS 67 | color: good 68 | 69 | - name: Notify slack tests failed 70 | if: failure() 71 | env: 72 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} 73 | uses: voxmedia/github-action-slack-notify-build@v1 74 | with: 75 | channel_id: C03E3JJGLQL 76 | status: FAILED 77 | color: danger 78 | -------------------------------------------------------------------------------- /.github/workflows/test-dkr-build.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker test container 2 | 3 | on: 4 | workflow_call 5 | 6 | env: 7 | REGISTRY: ghcr.io 8 | IMAGE_NAME: veraPDF/rest 9 | 10 | jobs: 11 | push_to_registry: 12 | if: github.repository == 'veraPDF/veraPDF-rest' 13 | name: Build and push Docker image to GitHub packages 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Log in to the Container registry 20 | uses: docker/login-action@v2 21 | with: 22 | registry: ${{ env.REGISTRY }} 23 | username: ${{ github.actor }} 24 | password: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: Extract metadata (tags, labels) for Docker 27 | id: meta 28 | uses: docker/metadata-action@v2 29 | with: 30 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 31 | 32 | - name: Build and push Docker image 33 | uses: docker/build-push-action@v2 34 | with: 35 | context: . 36 | push: true 37 | file: Dockerfile_dev 38 | tags: ghcr.io/verapdf/rest:pr-${{github.event.number}} 39 | labels: ${{ steps.meta.outputs.labels }} 40 | -------------------------------------------------------------------------------- /.github/workflows/test-dkr-image.yml: -------------------------------------------------------------------------------- 1 | name: Test Docker deployment 2 | 3 | on: 4 | workflow_call 5 | 6 | jobs: 7 | test: 8 | name: Deploy and test image 9 | runs-on: ubuntu-latest 10 | container: ubuntu 11 | services: 12 | verarest: 13 | image: ghcr.io/verapdf/rest:pr-${{ github.event.pull_request.number }} 14 | ports: 15 | - 8080:8080 16 | steps: 17 | - name: Install wget 18 | run: apt-get update; apt-get install wget -y 19 | - name: Test Profiles endpoint 20 | run: wget -O- http://verarest:8080/api/profiles 21 | - name: Test 1B profile endpoint 22 | run: wget -O- http://verarest:8080/api/profiles/1b 23 | -------------------------------------------------------------------------------- /.github/workflows/test-pr.yml: -------------------------------------------------------------------------------- 1 | name: PR QA 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | 7 | jobs: 8 | build: 9 | name: Checkout and Build 10 | runs-on: ubuntu-latest 11 | 12 | continue-on-error: true 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | java-version: [8, 11, 17, 21] 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: JDK setup 23 | uses: actions/setup-java@v4 24 | with: 25 | java-version: ${{ matrix.java-version }} 26 | distribution: 'temurin' 27 | cache: maven 28 | 29 | - name: Build with Maven 30 | run: mvn --batch-mode --update-snapshots verify 31 | 32 | docker_build: 33 | name: Build a test Docker image and add to GH container registry 34 | needs: [ build ] 35 | uses: veraPDF/veraPDF-rest/.github/workflows/test-dkr-build.yml@integration 36 | 37 | docker_test: 38 | name: Deploy and test the image built in previous step 39 | needs: [ docker_build ] 40 | uses: veraPDF/veraPDF-rest/.github/workflows/test-dkr-image.yml@integration 41 | -------------------------------------------------------------------------------- /.github/workflows/update-arlington-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Update arlington branch 2 | 3 | on: 4 | push: 5 | branches: 6 | - integration 7 | 8 | jobs: 9 | checkout-and-build: 10 | if: github.repository == 'veraPDF/veraPDF-rest' 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | java-version: [11, 17, 21] 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | ref: integration 24 | - name: JDK setup 25 | uses: actions/setup-java@v4 26 | continue-on-error: true 27 | with: 28 | java-version: ${{ matrix.java-version }} 29 | distribution: 'temurin' 30 | cache: maven 31 | - name: Fetch arlington branch and checkout 32 | run: | 33 | git fetch origin arlington:arlington 34 | git checkout -b test-branch arlington 35 | - name: Configure user name 36 | run: | 37 | git config user.name "Git User" 38 | git config user.email "user@test.com" 39 | - name: Add commit to the test branch 40 | run: git cherry-pick -m 1 ${{ github.event.before }}..${{ github.event.after }} --empty=drop 41 | - name: Build project with Maven 42 | if: success() 43 | run: mvn --batch-mode --update-snapshots verify 44 | 45 | merge: 46 | runs-on: ubuntu-latest 47 | needs: checkout-and-build 48 | steps: 49 | - name: Checkout code 50 | uses: actions/checkout@v4 51 | with: 52 | fetch-depth: 0 53 | ref: integration 54 | - name: Generate new branch name 55 | id: new-branch-name 56 | run: echo "branch_name=new-branch-$(date +%s)" >> "$GITHUB_OUTPUT" 57 | - name: Fetch arlington branch and checkout 58 | run: | 59 | git fetch origin arlington:arlington 60 | git checkout -b ${{ steps.new-branch-name.outputs.branch_name }} arlington 61 | - name: Configure user name 62 | run: | 63 | git config user.name "Git User" 64 | git config user.email "user@temp.com" 65 | - name: Add commit to new branch 66 | run: git cherry-pick -m 1 ${{ github.event.before }}..${{ github.event.after }} --empty=drop 67 | - name: Merge branch into arlington 68 | if: success() 69 | run: | 70 | git push origin ${{ steps.new-branch-name.outputs.branch_name }} 71 | git checkout arlington 72 | git merge ${{ steps.new-branch-name.outputs.branch_name }} 73 | git push origin arlington 74 | - name: Delete new branch 75 | run: git push origin --delete ${{ steps.new-branch-name.outputs.branch_name }} 76 | 77 | send-notification: 78 | runs-on: ubuntu-latest 79 | needs: [checkout-and-build, merge] 80 | if: | 81 | always() && 82 | github.repository == 'veraPDF/veraPDF-rest' && 83 | (contains(needs.*.result, 'failure') || 84 | contains(needs.*.result, 'skipped') || 85 | contains(needs.*.result, 'cancelled')) 86 | steps: 87 | - name: Send notification if build or merge failed 88 | env: 89 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} 90 | uses: voxmedia/github-action-slack-notify-build@v1 91 | with: 92 | channel_id: C03E3JJGLQL 93 | status: FAILED 94 | color: danger 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Maven artifacts 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | 12 | # Ignore VS code 13 | .vscode 14 | 15 | # Ignore Eclipse artifacts 16 | .project 17 | .settings 18 | .classpath 19 | bin/ 20 | 21 | # Ignore IDEA files 22 | .idea/ 23 | *.iml 24 | 25 | # Ignore pytest files 26 | .pytest_cache 27 | 28 | # Ignore external libraries 29 | bats-assert/ 30 | bats-support/ 31 | ./verapdf/ 32 | *.pyc 33 | *.log 34 | *.html 35 | *.css 36 | *.code-workspace 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # See https://docs.docker.com/engine/userguide/eng-image/multistage-build/ 2 | # First build the app on a maven open jdk 11 container 3 | FROM maven:3-eclipse-temurin-11-alpine AS app-builder 4 | RUN apk add --no-cache git 5 | WORKDIR /build 6 | 7 | # A specifc git branch, tag or commit to build from, defaults to master (release build) 8 | ARG GH_CHECKOUT 9 | ENV GH_CHECKOUT=${GH_CHECKOUT:-master} 10 | 11 | # Clone the repo, checkout the revision and build the application 12 | RUN git clone https://github.com/veraPDF/veraPDF-rest.git 13 | 14 | WORKDIR /build/veraPDF-rest 15 | RUN git checkout ${GH_CHECKOUT} && mvn clean package 16 | 17 | # Now build a Java JRE for the Alpine application image 18 | # https://github.com/docker-library/docs/blob/master/eclipse-temurin/README.md#creating-a-jre-using-jlink 19 | FROM eclipse-temurin:11-jdk-alpine as jre-builder 20 | 21 | # Create a custom Java runtime 22 | RUN "$JAVA_HOME/bin/jlink" \ 23 | --add-modules java.base,java.logging,java.xml,java.management,java.sql,java.desktop,jdk.crypto.ec \ 24 | --strip-debug \ 25 | --no-man-pages \ 26 | --no-header-files \ 27 | --compress=2 \ 28 | --output /javaruntime 29 | 30 | # Now the final application image 31 | FROM alpine:3 32 | 33 | # Specify the veraPDF REST version if you want to (to be used in build automation), default is 1.29.1 34 | ARG VERAPDF_REST_VERSION 35 | ENV VERAPDF_REST_VERSION=${VERAPDF_REST_VERSION:-1.29.1} 36 | 37 | # Set up dumb-init for process safety: https://github.com/Yelp/dumb-init 38 | ADD --link https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 /usr/local/bin/dumb-init 39 | RUN chmod +x /usr/local/bin/dumb-init 40 | 41 | # Copy the JRE from the previous stage 42 | ENV JAVA_HOME=/opt/java/openjdk 43 | ENV PATH "${JAVA_HOME}/bin:${PATH}" 44 | COPY --from=jre-builder /javaruntime $JAVA_HOME 45 | 46 | # Since this is a running network service we'll create an unprivileged account 47 | # which will be used to perform the rest of the work and run the actual service: 48 | RUN addgroup -S verapdf-rest && adduser --system -D --home /opt/verapdf-rest -G verapdf-rest verapdf-rest 49 | RUN mkdir --parents /var/opt/verapdf-rest/logs && chown -R verapdf-rest:verapdf-rest /var/opt/verapdf-rest 50 | 51 | USER verapdf-rest 52 | WORKDIR /opt/verapdf-rest 53 | 54 | # Copy the application from the previous stage 55 | COPY --from=app-builder /build/veraPDF-rest/target/verapdf-rest-${VERAPDF_REST_VERSION}.jar /opt/verapdf-rest/verapdf-rest.jar 56 | # Copy the default configuration file 57 | COPY --from=app-builder /build/veraPDF-rest/server.yml /var/opt/verapdf-rest/config/ 58 | COPY --from=app-builder /build/veraPDF-rest/config /opt/verapdf-rest/config/ 59 | 60 | VOLUME /var/opt/verapdf-rest 61 | EXPOSE 8080 62 | 63 | ENTRYPOINT [ "dumb-init", "--" ] 64 | CMD ["java", "-Djava.awt.headless=true", "-jar", "/opt/verapdf-rest/verapdf-rest.jar", "server", "/var/opt/verapdf-rest/config/server.yml"] 65 | -------------------------------------------------------------------------------- /Dockerfile_dev: -------------------------------------------------------------------------------- 1 | # See https://docs.docker.com/engine/userguide/eng-image/multistage-build/ 2 | # First build the app on a maven open jdk 11 container 3 | FROM maven:3-eclipse-temurin-11-alpine AS dev-builder 4 | WORKDIR /build 5 | 6 | # Copy the current dev source branch to a local build dir 7 | COPY src/ /build/veraPDF-rest/src/ 8 | COPY pom.xml /build/veraPDF-rest/ 9 | 10 | WORKDIR /build/veraPDF-rest 11 | RUN mvn clean package 12 | 13 | # Now build a Java JRE for the Alpine application image 14 | # https://github.com/docker-library/docs/blob/master/eclipse-temurin/README.md#creating-a-jre-using-jlink 15 | FROM eclipse-temurin:11-jdk-alpine as jre-builder 16 | 17 | # Create a custom Java runtime 18 | RUN "$JAVA_HOME/bin/jlink" \ 19 | --add-modules java.base,java.logging,java.xml,java.management,java.sql,java.desktop,jdk.crypto.ec \ 20 | --strip-debug \ 21 | --no-man-pages \ 22 | --no-header-files \ 23 | --compress=2 \ 24 | --output /javaruntime 25 | 26 | # Now the final application image 27 | FROM alpine:3 28 | 29 | # Specify the veraPDF REST version if you want to (to be used in build automation), default is 1.29.1 30 | ARG VERAPDF_REST_VERSION 31 | ENV VERAPDF_REST_VERSION=${VERAPDF_REST_VERSION:-1.29.1} 32 | 33 | # Set up dumb-init for process safety: https://github.com/Yelp/dumb-init 34 | ADD --link https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 /usr/local/bin/dumb-init 35 | RUN chmod +x /usr/local/bin/dumb-init 36 | 37 | # Copy the JRE from the previous stage 38 | ENV JAVA_HOME=/opt/java/openjdk 39 | ENV PATH "${JAVA_HOME}/bin:${PATH}" 40 | COPY --from=jre-builder /javaruntime $JAVA_HOME 41 | 42 | # Since this is a running network service we'll create an unprivileged account 43 | # which will be used to perform the rest of the work and run the actual service: 44 | RUN addgroup -S verapdf-rest && adduser --system -D --home /opt/verapdf-rest -G verapdf-rest verapdf-rest 45 | RUN mkdir --parents /var/opt/verapdf-rest/logs && chown -R verapdf-rest:verapdf-rest /var/opt/verapdf-rest 46 | 47 | USER verapdf-rest 48 | WORKDIR /opt/verapdf-rest 49 | # Copy the application from the previous stage 50 | COPY --from=dev-builder /build/veraPDF-rest/target/verapdf-rest-${VERAPDF_REST_VERSION}.jar /opt/verapdf-rest/verapdf-rest.jar 51 | 52 | # Copy the default configuration file 53 | COPY server.yml /var/opt/verapdf-rest/config/ 54 | COPY config /opt/verapdf-rest/config/ 55 | 56 | VOLUME /var/opt/verapdf-rest 57 | EXPOSE 8080 58 | 59 | ENTRYPOINT [ "dumb-init", "--" ] 60 | CMD ["java", "-Djava.awt.headless=true", "-jar", "/opt/verapdf-rest/verapdf-rest.jar", "server", "/var/opt/verapdf-rest/config/server.yml"] 61 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | 8 | [dev-packages] 9 | 10 | [requires] 11 | python_version = "3.9" 12 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "b20aaa9718024ec28b53570df5a504bc1765bb9490e31099da47c34bd616c357" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.python.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": {}, 19 | "develop": {} 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | veraPDF-rest 2 | ========================= 3 | 4 | *Dropwizard based veraPDF REST Services* 5 | 6 | Introduction 7 | ------------ 8 | This represents a development prototype, there's little in the way of exception handling and unit testing. 9 | The services are capable of serving up XML or JSON dependent upon the content type requested. 10 | 11 | ### Technologies 12 | The project's a Maven managed Java application, the application is based on 13 | [Dropwizard](https://www.dropwizard.io/en/stable/index.html), this brings together a set of reliable libraries, the 14 | following are most used and may prove informative if you are reading the code: 15 | 16 | * [Jetty](https://www.eclipse.org/jetty/) as a lean HTTP server, 17 | * [Jersey](https://jersey.java.net/) for REST services and associated, and 18 | * [Jackson](https://github.com/FasterXML/jackson/) for JSON and XML serialisation. 19 | 20 | A good place to get going is the [Dropwizard getting started guide](https://www.dropwizard.io/en/stable/getting-started.html). 21 | The [Dropwizard core documentation](https://www.dropwizard.io/en/stable/manual/core.html) covers the features used in the code base. 22 | 23 | Running DockerHub image 24 | -------------------- 25 | 26 | To run the veraPDF rest image from DockerHub: 27 | ``` 28 | docker run -d -p 8080:8080 -p 8081:8081 verapdf/rest:latest 29 | ``` 30 | 31 | Port 8080 serves both the veraPDF web interface and the veraPDF Rest API. Port 8081 serves the DropWizard diagnostics. 32 | 33 | Building and running locally 34 | -------------------- 35 | 36 | ### Docker 37 | This uses a Docker multi-stage build so the final container image which 38 | may be deployed does not require more than the base OpenJDK JRE without 39 | the entire build tool-chain. 40 | 41 | Tested lightly: 42 | 43 | ``` 44 | docker build -t verapdf-rest:latest . && docker run -d -p 8080:8080 -p 8081:8081 verapdf-rest:latest 45 | ``` 46 | 47 | If you encounter an error during docker run about "Can't set cookie dm_task_set_cookie failed", try: 48 | 49 | ``` 50 | sudo dmsetup udevcomplete_all 51 | ``` 52 | 53 | The built verapdf-rest image is notable smaller than just the base Maven image even before you consider the 54 | downloaded dependencies so the multi-stage build is definitely worthwhile: 55 | 56 | ``` 57 | cadams@ganymede:~ $ docker images 58 | REPOSITORY TAG IMAGE ID CREATED SIZE 59 | verapdf-rest latest c3c2a52a7bc0 5 minutes ago 297MB 60 | maven latest 88714384d642 11 days ago 749MB 61 | ``` 62 | 63 | Using the Alpine-based OpenJRE images provides a further hefty size reduction and we don't seem to be using 64 | anything which would be easier on Ubuntu: 65 | 66 | ``` 67 | verapdf-rest latest c69af6445b35 31 seconds ago 103MB 68 | ``` 69 | 70 | There's an "official" docker image that can be grabbed by `docker pull verapdf/rest:latest`. 71 | 72 | ### Kubernetes 73 | 74 | To use veraPDF-rest in as k8s deployment with load balancing and dynamic number of replicas (2 to 4) run the command: 75 | ``` 76 | kubectl apply -f kubernetes.yaml 77 | ``` 78 | 79 | ### Project structure 80 | Currently it's delivered as a single Maven module, veraPDF-rest. 81 | 82 | ### Swagger documentation 83 | Swagger documentation is available at [localhost:8080/swagger](http://localhost:8080/swagger). 84 | 85 | ### Want to try? 86 | First clone this project, go to the project directory, checkout to `master` branch for release version or `integration` 87 | branch for dev version, and then build the Maven project: 88 | 89 | git clone https://github.com/veraPDF/veraPDF-rest.git 90 | cd veraPDF-rest 91 | git checkout master 92 | mvn clean package 93 | 94 | To start up the server: 95 | 96 | java -jar target/verapdf-rest-1.28.1.jar server server.yml 97 | 98 | Go to [localhost:8080/api/info](http://localhost:8080/api/info) to see if the server is running, you should 99 | see something like: 100 | 101 | 102 | 103 | Linux 104 | 4.2.0-30-generic 105 | amd64 106 | 107 | 108 | Oracle Corporation 109 | 1.7.0_95 110 | x64 111 | /usr/lib/jvm/java-7-openjdk-amd64/jre 112 | 113 | 114 | 127.0.1.1 115 | dm-wrkstn 116 | 117 | 118 | 119 | 120 | You can also list the available validation profiles at 121 | [localhost:8080/api/profiles](http://localhost:8080/api/profiles): 122 | 123 | 124 | 125 | 1456384991133 126 | veraPDF Consortium 127 | PDF/A-1A validation profile 128 | Validation rules against ISO 19005-1:2005, Cor.1:2007 and Cor.2:2011 129 | 130 | 131 | 1456480484892 132 | veraPDF Consortium 133 | PDF/A-2B validation profile 134 | Validation rules against ISO 19005-2:2011 135 | 136 | 137 | 1456480579375 138 | veraPDF Consortium 139 | PDF/A-3B validation profile 140 | Validation rules against ISO 19005-3:2012 141 | 142 | 143 | 1456385033982 144 | veraPDF Consortium 145 | PDF/A-1B validation profile 146 | Validation rules against ISO 19005-1:2005, Cor.1:2007 and Cor.2:2011 147 | 148 | 149 | 150 | Services and curl tests 151 | ----------------------- 152 | There are a few services that you can test with [curl](https://curl.haxx.se/). 153 | 154 | ### API Environment service 155 | Shows some simple information about the server environment on [localhost:8080/api](http://localhost:8080/api) 156 | 157 | curl localhost:8080/api/info 158 | 159 | ### Validation Profile services 160 | Validation Profiles contain the PDF/A and PDF/UA validation tests and their description. A list of profile details is available 161 | at [localhost:8080/api/profiles/](http://localhost:8080/api/profiles/). To test with curl: 162 | 163 | curl localhost:8080/api/profiles 164 | 165 | Each profile is identified by a letter code made up the PDF/A or PDF/UA version and level. These are listed at 166 | [localhost:8080/api/profiles/ids/](http://localhost:8080/api/profiles/ids/): 167 | 168 | curl localhost:8080/api/profiles/ids 169 | 170 | An individual profile can be obtained by ID at `http://localhost:8080/api/profiles/*id*`, e.g. 171 | [localhost:8080/api/profiles/1b/](http://localhost:8080/api/profiles/1b/): 172 | 173 | curl localhost:8080/api/profiles/1b 174 | 175 | The curl call defaults to a JSON representation, to obtain the XML profile: 176 | 177 | curl localhost:8080/api/profiles/1b -H "Accept:application/xml" 178 | 179 | ### Validation services 180 | Validation is also available as a POST request at `http://localhost:8080/api/validate/*id*`. To test with curl: 181 | 182 | curl -F "file=@veraPDF-corpus/PDF_A-1b/6.1 File structure/6.1.12 Implementation limits/veraPDF test suite 6-1-12-t01-fail-a.pdf" localhost:8080/api/validate/1b 183 | 184 | or to obtain the result in XML: 185 | 186 | curl -F "file=@veraPDF-corpus/PDF_A-1b/6.1 File structure/6.1.12 Implementation limits/veraPDF test suite 6-1-12-t01-fail-a.pdf" localhost:8080/api/validate/1b -H "Accept:application/xml" 187 | 188 | Validation of PDF given by URL is available as a POST request `http://localhost:8080/api/validate/url/*id*`. To test with curl: 189 | 190 | ``` 191 | curl -F "url=http://www.pdf995.com/samples/pdf.pdf" localhost:8080/api/validate/url/1b 192 | ``` 193 | 194 | To validate your local files you need to add folder with files to the docker container. To run the veraPDF rest image 195 | with your local files run docker image with bind mount `-v /local/path/of/the/folder:/home/folder`. 196 | For example, to run the veraPDF rest image from DockerHub with your local files: 197 | 198 | ``` 199 | docker run -d -p 8080:8080 -p 8081:8081 -v /local/path/of/the/folder:/home/folder verapdf/rest:latest 200 | ``` 201 | 202 | and use curl: 203 | 204 | ``` 205 | curl -F "url=file:///home/folder/pdf.pdf" localhost:8080/api/validate/url/1b 206 | ``` 207 | 208 | To add file size in validation POST requests you need to send request with header (key `X-File-Size` and value in bytes). 209 | For example to use request `http://localhost:8080/api/validate/url/*id*` with file which size is 300 KB run: 210 | 211 | ``` 212 | curl -H "X-File-Size: 307200" -F "url=http://www.pdf995.com/samples/pdf.pdf" localhost:8080/api/validate/url/auto 213 | ``` 214 | 215 | ### veraPDF configuration parameters 216 | Configuration parameters are located in `/opt/verapdf-rest/config` folder of the container file system. The details on the veraPDF parameters are available at https://docs.verapdf.org/cli/config/. 217 | 218 | Additionally, this folder includes `server.yml` which contains [HTTP server configuration parameters](https://www.dropwizard.io/en/stable/manual/configuration.html) and additional parameters described below. 219 | 220 | #### Limiting PDF file size 221 | To set the maximum file size of PDF, change `maxFileSize` in `server.yml` file or run docker image as: 222 | ``` 223 | docker run -d -p 8080:8080 -p 8081:8081 -e VERAPDF_MAX_FILE_SIZE=1 verapdf/rest:latest 224 | ``` 225 | where VERAPDF_MAX_FILE_SIZE is 1MB. The default maximum PDF file size is 100MB. 226 | 227 | #### Maximum heap size 228 | To change maximum Java heap size in docker image run: 229 | ``` 230 | docker run -d -p 8080:8080 -p 8081:8081 -e JAVA_OPTS="-Xmx128M" verapdf/rest:latest 231 | ``` 232 | 233 | #### Additional configuration parameters 234 | See [Dropwizard Configuration Reference](https://www.dropwizard.io/en/stable/manual/configuration.html) for overview of available configuration parameters such as controlling the number of threads, queue size and others. 235 | -------------------------------------------------------------------------------- /charts/verapdf-rest/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/verapdf-rest/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: verapdf-rest 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "v1.28.1" 25 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "verapdf-rest.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "verapdf-rest.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "verapdf-rest.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "verapdf-rest.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") 20 | echo "Visit http://127.0.0.1:8080 to use your application" 21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "verapdf-rest.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "verapdf-rest.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "verapdf-rest.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "verapdf-rest.labels" -}} 37 | helm.sh/chart: {{ include "verapdf-rest.chart" . }} 38 | {{ include "verapdf-rest.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "verapdf-rest.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "verapdf-rest.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "verapdf-rest.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "verapdf-rest.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "verapdf-rest.fullname" . }} 5 | labels: 6 | {{- include "verapdf-rest.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "verapdf-rest.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "verapdf-rest.labels" . | nindent 8 }} 22 | {{- with .Values.podLabels }} 23 | {{- toYaml . | nindent 8 }} 24 | {{- end }} 25 | spec: 26 | {{- with .Values.imagePullSecrets }} 27 | imagePullSecrets: 28 | {{- toYaml . | nindent 8 }} 29 | {{- end }} 30 | serviceAccountName: {{ include "verapdf-rest.serviceAccountName" . }} 31 | securityContext: 32 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 33 | containers: 34 | - name: {{ .Chart.Name }} 35 | securityContext: 36 | {{- toYaml .Values.securityContext | nindent 12 }} 37 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 38 | imagePullPolicy: {{ .Values.image.pullPolicy }} 39 | ports: 40 | - name: http 41 | containerPort: {{ .Values.service.port }} 42 | protocol: TCP 43 | livenessProbe: 44 | httpGet: 45 | path: / 46 | port: 8080 47 | readinessProbe: 48 | httpGet: 49 | path: / 50 | port: 8080 51 | resources: 52 | {{- toYaml .Values.resources | nindent 12 }} 53 | {{- with .Values.volumeMounts }} 54 | volumeMounts: 55 | {{- toYaml . | nindent 12 }} 56 | {{- end }} 57 | {{- with .Values.volumes }} 58 | volumes: 59 | {{- toYaml . | nindent 8 }} 60 | {{- end }} 61 | {{- with .Values.nodeSelector }} 62 | nodeSelector: 63 | {{- toYaml . | nindent 8 }} 64 | {{- end }} 65 | {{- with .Values.affinity }} 66 | affinity: 67 | {{- toYaml . | nindent 8 }} 68 | {{- end }} 69 | {{- with .Values.tolerations }} 70 | tolerations: 71 | {{- toYaml . | nindent 8 }} 72 | {{- end }} 73 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "verapdf-rest.fullname" . }} 6 | labels: 7 | {{- include "verapdf-rest.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "verapdf-rest.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | target: 21 | type: Utilization 22 | averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 23 | {{- end }} 24 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 25 | - type: Resource 26 | resource: 27 | name: memory 28 | target: 29 | type: Utilization 30 | averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 31 | {{- end }} 32 | {{- end }} 33 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "verapdf-rest.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} 5 | {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} 6 | {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} 7 | {{- end }} 8 | {{- end }} 9 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} 10 | apiVersion: networking.k8s.io/v1 11 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 12 | apiVersion: networking.k8s.io/v1beta1 13 | {{- else -}} 14 | apiVersion: extensions/v1beta1 15 | {{- end }} 16 | kind: Ingress 17 | metadata: 18 | name: {{ $fullName }} 19 | labels: 20 | {{- include "verapdf-rest.labels" . | nindent 4 }} 21 | {{- with .Values.ingress.annotations }} 22 | annotations: 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | spec: 26 | {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} 27 | ingressClassName: {{ .Values.ingress.className }} 28 | {{- end }} 29 | {{- if .Values.ingress.tls }} 30 | tls: 31 | {{- range .Values.ingress.tls }} 32 | - hosts: 33 | {{- range .hosts }} 34 | - {{ . | quote }} 35 | {{- end }} 36 | secretName: {{ .secretName }} 37 | {{- end }} 38 | {{- end }} 39 | rules: 40 | {{- range .Values.ingress.hosts }} 41 | - host: {{ .host | quote }} 42 | http: 43 | paths: 44 | {{- range .paths }} 45 | - path: {{ .path }} 46 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} 47 | pathType: {{ .pathType }} 48 | {{- end }} 49 | backend: 50 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} 51 | service: 52 | name: {{ $fullName }} 53 | port: 54 | number: {{ $svcPort }} 55 | {{- else }} 56 | serviceName: {{ $fullName }} 57 | servicePort: {{ $svcPort }} 58 | {{- end }} 59 | {{- end }} 60 | {{- end }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "verapdf-rest.fullname" . }} 5 | labels: 6 | {{- include "verapdf-rest.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: 8080 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "verapdf-rest.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "verapdf-rest.serviceAccountName" . }} 6 | labels: 7 | {{- include "verapdf-rest.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | automountServiceAccountToken: {{ .Values.serviceAccount.automount }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /charts/verapdf-rest/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "verapdf-rest.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "verapdf-rest.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "verapdf-rest.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /charts/verapdf-rest/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for verapdf-rest. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: verapdf/rest 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart appVersion. 11 | tag: "" 12 | 13 | imagePullSecrets: [] 14 | nameOverride: "" 15 | fullnameOverride: "" 16 | 17 | serviceAccount: 18 | # Specifies whether a service account should be created 19 | create: true 20 | # Automatically mount a ServiceAccount's API credentials? 21 | automount: true 22 | # Annotations to add to the service account 23 | annotations: {} 24 | # The name of the service account to use. 25 | # If not set and create is true, a name is generated using the fullname template 26 | name: "" 27 | 28 | podAnnotations: {} 29 | podLabels: {} 30 | 31 | podSecurityContext: {} 32 | # fsGroup: 2000 33 | 34 | securityContext: {} 35 | # capabilities: 36 | # drop: 37 | # - ALL 38 | # readOnlyRootFilesystem: true 39 | # runAsNonRoot: true 40 | # runAsUser: 1000 41 | 42 | service: 43 | type: ClusterIP 44 | port: 8080 45 | 46 | ingress: 47 | enabled: false 48 | className: "" 49 | annotations: {} 50 | # kubernetes.io/ingress.class: nginx 51 | # kubernetes.io/tls-acme: "true" 52 | hosts: 53 | - host: chart-example.local 54 | paths: 55 | - path: / 56 | pathType: ImplementationSpecific 57 | tls: [] 58 | # - secretName: chart-example-tls 59 | # hosts: 60 | # - chart-example.local 61 | 62 | resources: {} 63 | # We usually recommend not to specify default resources and to leave this as a conscious 64 | # choice for the user. This also increases chances charts run on environments with little 65 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 66 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 67 | # limits: 68 | # cpu: 100m 69 | # memory: 128Mi 70 | # requests: 71 | # cpu: 100m 72 | # memory: 128Mi 73 | 74 | autoscaling: 75 | enabled: false 76 | minReplicas: 1 77 | maxReplicas: 100 78 | targetCPUUtilizationPercentage: 80 79 | # targetMemoryUtilizationPercentage: 80 80 | 81 | # Additional volumes on the output Deployment definition. 82 | volumes: [] 83 | # - name: foo 84 | # secret: 85 | # secretName: mysecret 86 | # optional: false 87 | 88 | # Additional volumeMounts on the output Deployment definition. 89 | volumeMounts: [] 90 | # - name: foo 91 | # mountPath: "/etc/foo" 92 | # readOnly: true 93 | 94 | nodeSelector: {} 95 | 96 | tolerations: [] 97 | 98 | affinity: {} 99 | -------------------------------------------------------------------------------- /config/app.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | fix 4 | https://github.com/veraPDF/veraPDF-validation-profiles/wiki 5 | 6 | 7 | -------------------------------------------------------------------------------- /config/features.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ACTION 5 | FORM_XOBJECT 6 | INFORMATION_DICTIONARY 7 | INTERACTIVE_FORM_FIELDS 8 | LOW_LEVEL_INFO 9 | 10 | 11 | -------------------------------------------------------------------------------- /config/fixer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /config/plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /config/validator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /kubernetes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion : apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: verapdf-rest-deployment 5 | labels: 6 | instance: veraPDF-rest 7 | part-of : veraPDF 8 | spec: 9 | selector: 10 | matchLabels: 11 | project: verapdf-rest 12 | template: 13 | metadata: 14 | labels: 15 | project: verapdf-rest 16 | spec: 17 | terminationGracePeriodSeconds: 300 18 | containers: 19 | - name : verapdf-rest 20 | image: ghcr.io/verapdf/rest:dev 21 | lifecycle: 22 | preStop: 23 | exec: 24 | command: ["/bin/sleep", "300"] 25 | resources: 26 | requests: 27 | cpu: "250m" 28 | limits: 29 | cpu: "2000m" 30 | ports: 31 | - containerPort: 8080 32 | name : rest-api-port 33 | - containerPort: 8081 34 | name : diagnostic-port 35 | 36 | --- 37 | apiVersion: autoscaling/v2 38 | kind: HorizontalPodAutoscaler 39 | metadata: 40 | name: verapdf-rest-autoscaling 41 | spec: 42 | scaleTargetRef: 43 | apiVersion: apps/v1 44 | kind: Deployment 45 | name: verapdf-rest-deployment 46 | minReplicas: 2 47 | maxReplicas: 4 48 | metrics: 49 | - type: Resource 50 | resource: 51 | name: cpu 52 | target: 53 | averageUtilization: 70 54 | type: Utilization 55 | 56 | --- 57 | apiVersion: v1 58 | kind: Service 59 | metadata: 60 | name: verapdf-rest-service 61 | labels: 62 | instance: veraPDF-rest 63 | part-of : veraPDF 64 | spec: 65 | selector: 66 | project: verapdf-rest 67 | ports: 68 | - name : rest-api-listener 69 | protocol : TCP 70 | port : 8080 71 | targetPort: 8080 72 | 73 | - name : diagnostic-listener 74 | protocol : TCP 75 | port : 8081 76 | targetPort: 8081 77 | type: LoadBalancer 78 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.verapdf 8 | verapdf-parent 9 | 1.29.1 10 | 11 | 12 | verapdf-rest 13 | 1.29.1 14 | 15 | veraPDF RESTful web services 16 | JAX-RS web services and client for veraPDF library 17 | 18 | 19 | 20 | https://github.com/veraPDF/veraPDF-rest/ 21 | scm:git:https://github.com/veraPDF/veraPDF-rest.git 22 | scm:git:git@github.com:veraPDF/veraPDF-rest.git 23 | 24 | 25 | https://github.com/veraPDF/veraPDF-rest/issues/ 26 | GitHub Issues 27 | 28 | 29 | 30 | 31 | 32 | true 33 | 34 | vera-dev 35 | Vera development 36 | https://artifactory.openpreservation.org/artifactory/vera-dev 37 | 38 | 39 | jcenter 40 | https://jcenter.bintray.com 41 | 42 | 43 | 44 | 45 | 2.1.7 46 | 2.10.0 47 | [1.29.0,1.30.0-RC) 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-shade-plugin 62 | 3.2.1 63 | 64 | true 65 | 66 | 67 | *:* 68 | 69 | META-INF/*.SF 70 | META-INF/*.DSA 71 | META-INF/*.RSA 72 | 73 | 74 | 75 | 76 | 77 | 78 | package 79 | 80 | shade 81 | 82 | 83 | 84 | 86 | 88 | org.verapdf.rest.app.VeraPdfRestApplication 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | src/main/resources 101 | true 102 | 103 | 104 | 105 | 106 | 107 | 108 | io.dropwizard 109 | dropwizard-core 110 | ${dropwizard.version} 111 | 112 | 113 | io.dropwizard 114 | dropwizard-forms 115 | ${dropwizard.version} 116 | 117 | 118 | com.fasterxml.jackson.jaxrs 119 | jackson-jaxrs-xml-provider 120 | ${fasterxml.version} 121 | compile 122 | 123 | 124 | com.fasterxml.jackson.dataformat 125 | jackson-dataformat-xml 126 | ${fasterxml.version} 127 | compile 128 | 129 | 130 | junit 131 | junit 132 | 4.13.1 133 | 134 | 135 | io.dropwizard 136 | dropwizard-views 137 | ${dropwizard.version} 138 | 139 | 140 | io.dropwizard 141 | dropwizard-views-mustache 142 | ${dropwizard.version} 143 | 144 | 145 | io.dropwizard 146 | dropwizard-assets 147 | ${dropwizard.version} 148 | 149 | 150 | com.smoketurner 151 | dropwizard-swagger 152 | 2.1.4-1 153 | 154 | 155 | 156 | commons-codec 157 | commons-codec 158 | 1.15 159 | 160 | 161 | 162 | commons-io 163 | commons-io 164 | 2.14.0 165 | 166 | 167 | 168 | org.verapdf 169 | validation-model 170 | ${verapdf.validation.version} 171 | 172 | 173 | 174 | com.fasterxml.jackson.core 175 | jackson-core 176 | 2.15.2 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-project-info-reports-plugin 186 | 2.4 187 | 188 | false 189 | false 190 | 191 | 192 | 193 | 194 | index 195 | dependencies 196 | project-team 197 | mailing-list 198 | cim 199 | issue-tracking 200 | license 201 | scm 202 | 203 | 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-javadoc-plugin 209 | 2.10.3 210 | 211 | public 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | github-rest 220 | 221 | 222 | github-rest 223 | GitHub Packages 224 | https://maven.pkg.github.com/veraPDF/veraPDF-rest 225 | 226 | 227 | github-rest 228 | GitHub Packages 229 | https://maven.pkg.github.com/veraPDF/veraPDF-rest 230 | 231 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | annotated-types==0.6.0 2 | black==24.3.0 3 | certifi==2024.7.4 4 | charset-normalizer==3.3.2 5 | click==8.1.7 6 | exceptiongroup==1.1.3 7 | idna==3.7 8 | iniconfig==2.0.0 9 | Jinja2==3.1.6 10 | loguru==0.7.2 11 | lxml==4.9.3 12 | MarkupSafe==2.1.3 13 | mypy-extensions==1.0.0 14 | packaging==23.2 15 | pathspec==0.11.2 16 | platformdirs==3.11.0 17 | pluggy==1.3.0 18 | pydantic==2.4.2 19 | pydantic-core==2.10.1 20 | pydantic-xml==2.4.0 21 | pydentic==0.0.1.dev3 22 | pytest==7.4.3 23 | pytest-base-url==2.1.0 24 | pytest-html==4.1.1 25 | pytest-metadata==3.0.0 26 | python-stdnum==1.19 27 | requests==2.32.0 28 | soupsieve==2.5 29 | tomli==2.0.1 30 | typing-extensions==4.8.0 31 | urllib3==2.2.2 32 | xmltodict==0.13.0 33 | -------------------------------------------------------------------------------- /server.yml: -------------------------------------------------------------------------------- 1 | server: 2 | applicationConnectors: 3 | - type: http 4 | port: 8080 5 | adminConnectors: 6 | - type: http 7 | port: 8081 8 | logging: 9 | level: INFO 10 | loggers: 11 | "org.verapdf": 12 | level: DEBUG 13 | additive: false 14 | appenders: 15 | - type: file 16 | currentLogFilename: /var/opt/verapdf-rest/logs/verapdf-rest.log 17 | threshold: ALL 18 | queueSize: 512 19 | discardingThreshold: 0 20 | archive: true 21 | archivedLogFilenamePattern: /var/opt/verapdf-rest/logs/verapdf-rest-%d.log 22 | archivedFileCount: 20 23 | timeZone: CET 24 | logFormat: "%-5p [%d{ISO8601,UTC}] %c: %m%n%rEx" 25 | bufferSize: 8KiB 26 | immediateFlush: true 27 | appenders: 28 | - type: console 29 | maxFileSize: ${VERAPDF_MAX_FILE_SIZE:-100} 30 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | github-rest 8 | 9 | 10 | 11 | 12 | github-rest 13 | 14 | github-rest::default::https://maven.pkg.github.com/veraPDF/veraPDF-rest/ 15 | github-rest::default::https://maven.pkg.github.com/veraPDF/veraPDF-rest/ 16 | 17 | 18 | 19 | 20 | 21 | 22 | github-rest 23 | MaximPlusov 24 | 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 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/org/openpreservation/bytestreams/ByteStreamId.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.openpreservation.bytestreams; 5 | 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 8 | 9 | 10 | /** 11 | * Encapsulates two byte stream properties: 12 | *
    13 | *
  • The length of the ByteStream in bytes, will be >= 0.
  • 14 | *
  • The hex encoded SHA1 digest calculated from the ByteStream, a 40 15 | * character hex string, i.e. [0-9a-f]{40}.
  • 16 | *
17 | * Used as an identifier for tagging richer metadata and associating it with a 18 | * ByteStream. 19 | * 20 | * @author Carl Wilson 21 | */ 22 | @JsonSerialize(as = ByteStreamIdImpl.class) 23 | @JsonDeserialize(as = ByteStreamIdImpl.class) 24 | public interface ByteStreamId { 25 | /** 26 | * @return The length of the ByteStream in bytes, will be >= 0. 27 | */ 28 | public long getLength(); 29 | 30 | /** 31 | * @return The hex encoded SHA1 digest calculated from the ByteStream, a 32 | * 40 character hex string, i.e. [0-9a-f]{40}. 33 | */ 34 | public String getHexSHA1(); 35 | 36 | /** 37 | * @param id 38 | * id for comparison 39 | * @return -1 if the item less than id, 0 if equal, 1 if greater 40 | */ 41 | public int compareTo(final ByteStreamId id); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/openpreservation/bytestreams/ByteStreamIdImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.openpreservation.bytestreams; 5 | 6 | 7 | /** 8 | * Straightforward immutable implementation of the {@link ByteStreamId} interface. 9 | * 10 | * @author Carl Wilson { 13 | static final ByteStreamIdImpl UNIDENTIFIED_STREAM = new ByteStreamIdImpl(); 14 | static final ByteStreamIdImpl NULL_STREAM_ID = new ByteStreamIdImpl( 15 | 0L, ByteStreams.NULL_SHA1.toLowerCase()); 16 | 17 | private final String hexSHA1; 18 | private final long length; 19 | 20 | private ByteStreamIdImpl() { 21 | this(ByteStreams.UNKNOWN_LENGTH, ByteStreams.NULL_SHA1.toLowerCase()); 22 | } 23 | 24 | private ByteStreamIdImpl(final long length, final String sha1) { 25 | this.length = length; 26 | this.hexSHA1 = sha1; 27 | } 28 | 29 | static final ByteStreamIdImpl fromValues(final long length, 30 | final String sha1) { 31 | if (length < 1L) 32 | throw new IllegalArgumentException("(length " + length //$NON-NLS-1$ 33 | + " < 1) == true"); //$NON-NLS-1$ 34 | if ((sha1 == null) || (sha1.isEmpty())) 35 | throw new IllegalArgumentException( 36 | "((sha1 == null) || (sha1.isEmpty())) == true"); //$NON-NLS-1$ 37 | if (!ByteStreams.isHexSHA1(sha1)) 38 | throw new IllegalArgumentException( 39 | "ByteStreams.isHexSHA1(sha1) != true, regex used: " //$NON-NLS-1$ 40 | + ByteStreams.HEX_SHA1_REGEX); 41 | // This is the only route to the constructor, lower case the hash values 42 | // to prevent 43 | // case sensitivity ruining equals and hashCode. Upper and lower case 44 | // hex digest strings 45 | // are equivalent. 46 | return new ByteStreamIdImpl(length, sha1.toLowerCase()); 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public final long getLength() { 54 | return this.length; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public final String getHexSHA1() { 62 | return this.hexSHA1; 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public String toString() { 70 | return "{\"ByteStreamId\":{\"hexSHA1\":\"" + this.hexSHA1 + "\",\"length\":" + this.length + " bytes}}"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 71 | } 72 | 73 | /** 74 | * {@inheritDoc} 75 | */ 76 | @Override 77 | public final boolean equals(final Object obj) { 78 | if (this == obj) 79 | return true; 80 | if (!(obj instanceof ByteStreamId)) 81 | return false; 82 | ByteStreamId other = (ByteStreamId) obj; 83 | if (this.length != other.getLength()) 84 | return false; 85 | if (this.hexSHA1 == null) { 86 | if (other.getHexSHA1() != null) 87 | return false; 88 | } else if (!this.hexSHA1.equals(other.getHexSHA1())) 89 | return false; 90 | return true; 91 | } 92 | 93 | /** 94 | * {@inheritDoc} 95 | */ 96 | @Override 97 | public final int hashCode() { 98 | final int prime = 31; 99 | int result = 1; 100 | result = prime * result + (int) (this.length ^ (this.length >>> 32)); 101 | result = prime * result 102 | + ((this.hexSHA1 == null) ? 0 : this.hexSHA1.hashCode()); 103 | return result; 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | @Override 110 | public final int compareTo(final ByteStreamId other) { 111 | long lengthDiff = this.length - other.getLength(); 112 | if (lengthDiff != 0) 113 | return (lengthDiff > 0) ? 1 : -1; 114 | // Sort by 256 hash 115 | return this.hexSHA1.compareToIgnoreCase(other 116 | .getHexSHA1()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/org/openpreservation/bytestreams/ByteStreams.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.openpreservation.bytestreams; 5 | 6 | import java.io.BufferedInputStream; 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.security.DigestInputStream; 13 | import java.security.MessageDigest; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.util.regex.Matcher; 16 | import java.util.regex.Pattern; 17 | 18 | import org.apache.commons.codec.binary.Hex; 19 | 20 | /** 21 | * A static factory and utility class for {@link ByteStreamId ByteStreamIds}. 22 | * This is the only way to instantiate ByteStreamIds and provides methods to 23 | * create them from values, a file and an InputStream. 24 | * 25 | * The class also provides methods to check to see if a Strings pattern matches 26 | * that of a hex SHA-1 hash String and another to format Byte lengths into 27 | * readable strings. 28 | * 29 | * @author Carl Wilson -1) { 135 | totalBytes += bytesRead; 136 | } 137 | // Return the new instance from the calculated details 138 | return (totalBytes == 0L) ? ByteStreams.nullByteStreamId() 139 | : ByteStreamIdImpl.fromValues(totalBytes, 140 | Hex.encodeHexString(SHA1.digest())); 141 | } 142 | 143 | /** 144 | * Factory method to create a byte sequence from the contents of a file. 145 | * 146 | * @param file 147 | * a java.io.File from which to create the ByteStreamId 148 | * @return a new ByteStreamId instance created from the file 149 | * @throws FileNotFoundException 150 | * when the file cannot be found 151 | * @throws IOException 152 | * when the InputStream opened from the file cannot be read 153 | */ 154 | public static final ByteStreamId idFromFile(final File file) 155 | throws FileNotFoundException, IOException { 156 | if (file == null) 157 | throw new IllegalArgumentException("file == null"); //$NON-NLS-1$ 158 | if (file.isDirectory()) 159 | throw new IllegalArgumentException("file.isDirectory() == true"); //$NON-NLS-1$ 160 | try (InputStream inStream = new FileInputStream(file)) { 161 | ByteStreamId bs = idFromStream(inStream); 162 | return bs; 163 | } 164 | } 165 | 166 | /** 167 | * Static method to test whether a string matches the pattern for a hex 168 | * SHA-1 digest, that is 40 hex characters, i.e. only 0-9 or A-F case 169 | * insensitive. Effectively runs a Java RegEx on the string. 170 | * 171 | * @param toTest 172 | * java.lang.String to test to see if it's a hex SHA-1 string 173 | * @return true if the String matches the hex SHA-1 RegEx, false if not. 174 | */ 175 | public static final boolean isHexSHA1(final String toTest) { 176 | if (toTest == null) 177 | throw new IllegalArgumentException("toTest == null"); //$NON-NLS-1$ 178 | Matcher matcher = HEX_SHA1_PATTERN.matcher(toTest); 179 | return matcher.find(); 180 | } 181 | 182 | /** 183 | * Returns a human readable Byte count with units, safe up to ExaBytes 184 | * @param bytes 185 | * the number of bytes as a long 186 | * @param si 187 | * set true to use SI units and 1000 bytes == 1K, false 1024 188 | * bytes == 1K 189 | * @return a human readable byte count derived from bytes 190 | */ 191 | public static String humanReadableByteCount(final long bytes, 192 | final boolean si) { 193 | int unit = si ? 1000 : 1024; 194 | if (bytes < unit) 195 | return bytes + " B"; //$NON-NLS-1$ 196 | int exp = (int) (Math.log(bytes) / Math.log(unit)); 197 | String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) //$NON-NLS-1$ //$NON-NLS-2$ 198 | + (si ? "" : "i"); //$NON-NLS-1$ //$NON-NLS-2$ 199 | return String.format( 200 | "%.1f %sB", Double.valueOf(bytes / Math.pow(unit, exp)), pre); //$NON-NLS-1$ 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/org/openpreservation/bytestreams/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Carl Wilson.

3 | */ 4 | 5 | package org.openpreservation.bytestreams; -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/app/VeraPdfRestApplication.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.app; 5 | 6 | import java.util.EnumSet; 7 | 8 | import javax.servlet.DispatcherType; 9 | import javax.servlet.FilterRegistration; 10 | 11 | import io.federecio.dropwizard.swagger.SwaggerBundle; 12 | import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration; 13 | import io.dropwizard.configuration.EnvironmentVariableSubstitutor; 14 | import io.dropwizard.configuration.SubstitutingSourceProvider; 15 | import org.eclipse.jetty.servlets.CrossOriginFilter; 16 | import org.verapdf.rest.resources.ApiResource; 17 | import org.verapdf.rest.resources.HomePageResource; 18 | import org.verapdf.rest.resources.ValidateResource; 19 | import org.verapdf.rest.resources.ValidationExceptionMapper; 20 | 21 | import io.dropwizard.Application; 22 | import io.dropwizard.assets.AssetsBundle; 23 | import io.dropwizard.forms.MultiPartBundle; 24 | import io.dropwizard.setup.Bootstrap; 25 | import io.dropwizard.setup.Environment; 26 | import io.dropwizard.views.ViewBundle; 27 | 28 | /** 29 | * @author Carl Wilson 30 | * 31 | */ 32 | public class VeraPdfRestApplication extends Application { 33 | 34 | private static final String NAME = "verapdf-rest"; //$NON-NLS-1$ 35 | 36 | /** 37 | * Main method for Jetty server application. Simply calls the run method 38 | * with command line args. 39 | * 40 | * @param args 41 | * command line arguments as string array. 42 | * @throws Exception 43 | * passes any exception thrown by run 44 | */ 45 | public static void main(String[] args) throws Exception { 46 | new VeraPdfRestApplication().run(args); 47 | } 48 | 49 | @Override 50 | public String getName() { 51 | return NAME; 52 | } 53 | 54 | @Override 55 | public void initialize(Bootstrap bootstrap) { 56 | bootstrap.addBundle(new MultiPartBundle()); 57 | bootstrap.addBundle(new ViewBundle<>()); 58 | bootstrap.addBundle(new SwaggerBundle() { 59 | @Override 60 | protected SwaggerBundleConfiguration getSwaggerBundleConfiguration( 61 | VeraPdfRestConfiguration configuration) { 62 | SwaggerBundleConfiguration config = new SwaggerBundleConfiguration(); 63 | config.setResourcePackage("org.verapdf.rest.resources"); 64 | 65 | return config; 66 | } 67 | }); 68 | bootstrap.setConfigurationSourceProvider( 69 | new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(), 70 | new EnvironmentVariableSubstitutor(false) 71 | )); 72 | bootstrap.addBundle(new AssetsBundle("/assets/css", "/css", null, "css")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 73 | bootstrap.addBundle(new AssetsBundle("/assets/js", "/js", null, "js")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 74 | bootstrap.addBundle(new AssetsBundle("/assets/img", "/img", null, "img")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 75 | // //$NON-NLS- 76 | } 77 | 78 | @Override 79 | public void run(VeraPdfRestConfiguration configuration, 80 | Environment environment) { 81 | // Create & register our REST resources 82 | final ValidationExceptionMapper vem = new ValidationExceptionMapper(); 83 | ValidateResource validateResource = new ValidateResource(); 84 | ValidateResource.setMaxFileSize(configuration.getMaxFileSize()); 85 | environment.jersey().register(validateResource); 86 | environment.jersey().register(new ApiResource()); 87 | environment.jersey().register(new HomePageResource()); 88 | environment.jersey().register(vem); 89 | // Set up cross domain REST 90 | setupCORS(environment); 91 | } 92 | 93 | private static void setupCORS(Environment environment) { 94 | // Enable CORS headers 95 | final FilterRegistration.Dynamic cors = environment.servlets() 96 | .addFilter("CORS", CrossOriginFilter.class); //$NON-NLS-1$ 97 | 98 | // Configure CORS parameters 99 | cors.setInitParameter("allowedOrigins", "*"); //$NON-NLS-1$ //$NON-NLS-2$ 100 | cors.setInitParameter( 101 | "allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin"); //$NON-NLS-1$ //$NON-NLS-2$ 102 | cors.setInitParameter( 103 | "allowedMethods", "OPTIONS,GET,PUT,POST,DELETE,HEAD"); //$NON-NLS-1$ //$NON-NLS-2$ 104 | 105 | // Add URL mapping 106 | cors.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), 107 | true, "/*"); //$NON-NLS-1$ 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/app/VeraPdfRestConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.app; 5 | 6 | import io.dropwizard.Configuration; 7 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | 10 | /** 11 | * Configuration object for the Dropwizard app. Reads defaults from 12 | * configuration YAML file. This class has to be "mutable" due to Dropwizard 13 | * requirements. 14 | * 15 | * @author Carl Wilson.

16 | */ 17 | public class VeraPdfRestConfiguration extends Configuration { 18 | private int port; 19 | private int maxFileSize; 20 | 21 | @JsonProperty 22 | public int getMaxFileSize() { 23 | return maxFileSize; 24 | } 25 | 26 | @JsonProperty 27 | public void setMaxFileSize(int maxFileSize) { 28 | this.maxFileSize = maxFileSize; 29 | } 30 | 31 | /** 32 | * @return the TCP/IP port used 33 | */ 34 | @JsonProperty 35 | public int getPort() { 36 | return this.port; 37 | } 38 | 39 | /** 40 | * @param port 41 | * numeric value of TCP/IP port to listen to 42 | */ 43 | @JsonProperty 44 | public void setPort(int port) { 45 | this.port = port; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/environment/Environment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.environment; 5 | 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 8 | 9 | /** 10 | * @author Carl Wilson.

11 | */ 12 | @JacksonXmlRootElement 13 | public class Environment { 14 | final static Environment INSTANCE = new Environment(); 15 | private final ServerDetails hardware = ServerDetails.getInstance(); 16 | private final OsDetails os = OsDetails.getInstance(); 17 | private final JvmDetails java = JvmDetails.getInstance(); 18 | 19 | private Environment() { 20 | // Do nothing 21 | } 22 | 23 | /** 24 | * @return the Server details 25 | */ 26 | @JsonProperty 27 | public ServerDetails getServer() { 28 | return this.hardware; 29 | } 30 | 31 | /** 32 | * @return the os details 33 | */ 34 | @JsonProperty 35 | public OsDetails getOS() { 36 | return this.os; 37 | } 38 | 39 | /** 40 | * @return the Java details 41 | */ 42 | @JsonProperty 43 | public JvmDetails getJava() { 44 | return this.java; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/environment/Environments.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.environment; 5 | 6 | import java.net.InetAddress; 7 | import java.net.NetworkInterface; 8 | import java.net.SocketException; 9 | import java.net.UnknownHostException; 10 | import java.text.DateFormat; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Date; 13 | 14 | /** 15 | * Static utility class that gathers environment details for reporting. 16 | * 17 | * @author Carl Wilson carlwilson AT github 19 | * @version 0.1 20 | * 21 | * Created 27 Jul 2012:12:56:47 22 | */ 23 | 24 | @SuppressWarnings("boxing") 25 | public final class Environments { 26 | static final String JAVA_CPU_ISA_PROP = "sun.cpu.isalist"; //$NON-NLS-1$ 27 | static final String JAVA_ARCH_PROP = "sun.arch.data.model"; //$NON-NLS-1$ 28 | static final String JAVA_PROP_ROOT = "java"; //$NON-NLS-1$ 29 | static final String JAVA_VM_PROP_ROOT = JAVA_PROP_ROOT + ".vm"; //$NON-NLS-1$ 30 | static final String JAVA_HOME_PROP = JAVA_PROP_ROOT + ".home"; //$NON-NLS-1$ 31 | static final String JAVA_VM_VENDOR_PROP = JAVA_VM_PROP_ROOT + ".vendor"; //$NON-NLS-1$ 32 | static final String JAVA_VERSION_PROP = JAVA_PROP_ROOT + ".version"; //$NON-NLS-1$ 33 | static final String USER_PROP_ROOT = "user"; //$NON-NLS-1$ 34 | static final String USER_NAME_PROP = USER_PROP_ROOT + ".name"; //$NON-NLS-1$ 35 | static final String USER_HOME_PROP = USER_PROP_ROOT + ".home"; //$NON-NLS-1$ 36 | static final String USER_COUNTRY_PROP = USER_PROP_ROOT + ".country"; //$NON-NLS-1$ 37 | static final String USER_LANGUAGE_PROP = USER_PROP_ROOT + ".language"; //$NON-NLS-1$ 38 | static final String OS_PROP_ROOT = "os"; //$NON-NLS-1$ 39 | static final String OS_NAME_PROP = OS_PROP_ROOT + ".name"; //$NON-NLS-1$ 40 | static final String OS_VERSION_PROP = OS_PROP_ROOT + ".version"; //$NON-NLS-1$ 41 | static final String OS_ARCH_PROP = OS_PROP_ROOT + ".arch"; //$NON-NLS-1$ 42 | static final String HOST_NAME; 43 | static final String HOST_ADDRESS; 44 | static final String MACH_ADDRESS; 45 | static final String CPU_ISA; 46 | static final String JAVA_ARCH; 47 | static final String JAVA_HOME; 48 | static final String JAVA_VENDOR; 49 | static final String JAVA_VERSION; 50 | static final String USER_NAME; 51 | static final String USER_HOME; 52 | static final String USER_COUNTRY; 53 | static final String USER_LANGUAGE; 54 | static final String OS_NAME; 55 | static final String OS_VERSION; 56 | static final String OS_ARCH; 57 | static { 58 | try { 59 | InetAddress address = InetAddress.getLocalHost(); 60 | HOST_NAME = address.getHostName(); 61 | HOST_ADDRESS = address.getHostAddress(); 62 | NetworkInterface nwi = NetworkInterface.getByInetAddress(address); 63 | String machAddress = ""; //$NON-NLS-1$ 64 | if (nwi != null) { 65 | byte[] mac = nwi.getHardwareAddress(); 66 | if (mac != null) { 67 | StringBuilder sb = new StringBuilder(); 68 | for (int i = 0; i < mac.length; i++) { 69 | sb.append(String 70 | .format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 71 | } 72 | machAddress = sb.toString(); 73 | } 74 | } 75 | MACH_ADDRESS = machAddress; 76 | CPU_ISA = System.getProperty(JAVA_CPU_ISA_PROP); 77 | JAVA_ARCH = "x" + System.getProperty(JAVA_ARCH_PROP); //$NON-NLS-1$ 78 | JAVA_HOME = System.getProperty(JAVA_HOME_PROP); 79 | JAVA_VENDOR = System.getProperty(JAVA_VM_VENDOR_PROP); 80 | JAVA_VERSION = System.getProperty(JAVA_VERSION_PROP); 81 | USER_NAME = System.getProperty(USER_NAME_PROP); 82 | USER_HOME = System.getProperty(USER_HOME_PROP); 83 | USER_COUNTRY = System.getProperty(USER_COUNTRY_PROP); 84 | USER_LANGUAGE = System.getProperty(USER_LANGUAGE_PROP); 85 | OS_NAME = System.getProperty(OS_NAME_PROP); 86 | OS_VERSION = System.getProperty(OS_VERSION_PROP); 87 | OS_ARCH = System.getProperty(OS_ARCH_PROP); 88 | } catch (UnknownHostException excep) { 89 | throw new IllegalStateException( 90 | "Illegal length IP address, check your setup.", excep); //$NON-NLS-1$ 91 | } catch (SocketException excep) { 92 | throw new IllegalStateException( 93 | "Socket Exception when finding MACH address.", excep); //$NON-NLS-1$ 94 | } 95 | } 96 | /** ISO Date pattern for ISO 8601 Date */ 97 | static final String ISO8601_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; //$NON-NLS-1$ 98 | /** {@link DateFormat} for ISO8601 date */ 99 | static DateFormat IOS8601_DATE_FORMATTER = new SimpleDateFormat( 100 | ISO8601_DATE_PATTERN); 101 | 102 | private Environments() { 103 | throw new AssertionError("In Environments no-arg constructor."); //$NON-NLS-1$ 104 | } 105 | 106 | /** 107 | * @return the Environment instance 108 | */ 109 | public static final Environment getEnvironment() { 110 | return Environment.INSTANCE; 111 | } 112 | 113 | /** 114 | * @return the machine host name 115 | */ 116 | public static final String getHostName() { 117 | return HOST_NAME; 118 | } 119 | 120 | /** 121 | * @return the hosts ip address 122 | */ 123 | public static final String getHostAddress() { 124 | return HOST_ADDRESS; 125 | } 126 | 127 | /** 128 | * @return the hosts mach address 129 | */ 130 | public static final String getMachAddress() { 131 | return MACH_ADDRESS; 132 | } 133 | 134 | /** 135 | * @return the CPU details 136 | */ 137 | public static final String getCPUIsa() { 138 | return CPU_ISA; 139 | } 140 | 141 | /** 142 | * @return summary of host details 143 | */ 144 | public static final String getHostSummary() { 145 | return "{\"name\":\"" + Environments.getHostName() + "\",\"CPU\":\"" + CPU_ISA //$NON-NLS-1$ //$NON-NLS-2$ 146 | + "\"}"; //$NON-NLS-1$ 147 | } 148 | 149 | /** 150 | * @return java vendor 151 | */ 152 | public static final String getJavaVendor() { 153 | return JAVA_VENDOR; 154 | } 155 | 156 | /** 157 | * @return java version 158 | */ 159 | public static final String getJavaVersion() { 160 | return JAVA_VERSION; 161 | } 162 | 163 | /** 164 | * @return java version 165 | */ 166 | public static final String getJavaArch() { 167 | return JAVA_ARCH; 168 | } 169 | 170 | /** 171 | * @return java home 172 | */ 173 | public static final String getJavaHome() { 174 | return JAVA_HOME; 175 | } 176 | 177 | /** 178 | * @return summary of java details 179 | */ 180 | public static final String getJavaSummary() { 181 | return "Java [vendor:" + JAVA_VENDOR + ", version:" + JAVA_VERSION //$NON-NLS-1$ //$NON-NLS-2$ 182 | + JAVA_ARCH + ", home:" + JAVA_HOME + "]"; //$NON-NLS-1$ //$NON-NLS-2$ 183 | } 184 | 185 | /** 186 | * @return user name 187 | */ 188 | public static final String getUserName() { 189 | return USER_NAME; 190 | } 191 | 192 | /** 193 | * @return user home dir 194 | */ 195 | public static final String getUserHome() { 196 | return USER_HOME; 197 | } 198 | 199 | /** 200 | * @return user country 201 | */ 202 | public static final String getUserCountry() { 203 | return USER_COUNTRY; 204 | } 205 | 206 | /** 207 | * @return user language 208 | */ 209 | public static final String getUserLanguage() { 210 | return USER_LANGUAGE; 211 | } 212 | 213 | /** 214 | * @return summary of user details 215 | */ 216 | public static final String getUserSummary() { 217 | return "user [name:" + USER_NAME + ", country:" + USER_COUNTRY //$NON-NLS-1$ //$NON-NLS-2$ 218 | + ", lang:" + USER_LANGUAGE + ", home:" + USER_HOME + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 219 | } 220 | 221 | /** 222 | * @return the operating system name 223 | */ 224 | public static final String getOSName() { 225 | return OS_NAME; 226 | } 227 | 228 | /** 229 | * @return the operating system verison 230 | */ 231 | public static final String getOSVersion() { 232 | return OS_VERSION; 233 | } 234 | 235 | /** 236 | * @return the operating system architecture 237 | */ 238 | public static final String getOSArch() { 239 | return OS_ARCH; 240 | } 241 | 242 | /** 243 | * @return a summary of os details 244 | */ 245 | public static final String getOSSummary() { 246 | return "os [name:" + OS_NAME + ", version:" + OS_VERSION + ", arch:" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 247 | + OS_ARCH + "]"; //$NON-NLS-1$ 248 | } 249 | 250 | /** 251 | * @param date 252 | * @return the date as an ISO formatted string 253 | */ 254 | public static final String toISO8601String(Date date) { 255 | return IOS8601_DATE_FORMATTER.format(date); 256 | } 257 | 258 | /** 259 | * @return a summary of the detected Environments. 260 | */ 261 | public static final String getSummary() { 262 | return getHostSummary() + "\n" + getJavaSummary() + "\n" //$NON-NLS-1$ //$NON-NLS-2$ 263 | + getUserSummary() + "\n" + getOSSummary(); //$NON-NLS-1$ 264 | } 265 | 266 | /** 267 | * @return true if OS is windows 268 | */ 269 | public static boolean isWindows() { 270 | return (OS_NAME.toLowerCase().indexOf("win") >= 0); //$NON-NLS-1$ 271 | } 272 | 273 | /** 274 | * @return true if OS is mac 275 | */ 276 | public static boolean isMac() { 277 | return (OS_NAME.toLowerCase().indexOf("mac") >= 0); //$NON-NLS-1$ 278 | } 279 | 280 | /** 281 | * @return true if os is *nix 282 | */ 283 | public static boolean isUnix() { 284 | return (OS_NAME.toLowerCase().indexOf("nix") >= 0 || OS_NAME.indexOf("nux") >= 0); //$NON-NLS-1$ //$NON-NLS-2$ 285 | } 286 | 287 | /** 288 | * @return true if OS is solaris 289 | */ 290 | public static boolean isSolaris() { 291 | return (OS_NAME.toLowerCase().indexOf("sunos") >= 0); //$NON-NLS-1$ 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/environment/JvmDetails.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.environment; 5 | 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 8 | 9 | 10 | 11 | /** 12 | * @author Carl Wilson.

13 | */ 14 | @JacksonXmlRootElement 15 | public class JvmDetails { 16 | private final static JvmDetails INSTANCE = new JvmDetails(); 17 | private final String vendor; 18 | private final String version; 19 | private final String architecture; 20 | private final String home; 21 | 22 | private JvmDetails() { 23 | this.vendor = Environments.getJavaVendor(); 24 | this.version = Environments.getJavaVersion(); 25 | this.architecture = Environments.getJavaArch(); 26 | this.home = Environments.getJavaHome(); 27 | } 28 | 29 | /** 30 | * @return the JVM details instance 31 | */ 32 | public static final JvmDetails getInstance() { 33 | return INSTANCE; 34 | } 35 | 36 | /** 37 | * @return the Java vendor 38 | */ 39 | @JsonProperty 40 | public String getVendor() { 41 | return this.vendor; 42 | } 43 | 44 | /** 45 | * @return the Java version 46 | */ 47 | @JsonProperty 48 | public String getVersion() { 49 | return this.version; 50 | } 51 | 52 | /** 53 | * @return the Java system architecture 54 | */ 55 | @JsonProperty 56 | public String getArchitecture() { 57 | return this.architecture; 58 | } 59 | 60 | /** 61 | * @return the value of JAVA_HOME 62 | */ 63 | @JsonProperty 64 | public String getHome() { 65 | return this.home; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/environment/OsDetails.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.environment; 5 | 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 8 | 9 | 10 | 11 | /** 12 | * @author Carl Wilson.

13 | */ 14 | @JacksonXmlRootElement 15 | public class OsDetails { 16 | private final static OsDetails INSTANCE = new OsDetails(); 17 | private final String name; 18 | private final String version; 19 | private final String architecture; 20 | 21 | private OsDetails() { 22 | this.name = Environments.getOSName(); 23 | this.version = Environments.getOSVersion(); 24 | this.architecture = Environments.getOSArch(); 25 | } 26 | 27 | /** 28 | * @return the OS Details instance 29 | */ 30 | public final static OsDetails getInstance() { 31 | return INSTANCE; 32 | } 33 | 34 | /** 35 | * @return the os name 36 | */ 37 | @JsonProperty 38 | public String getName() { 39 | return this.name; 40 | } 41 | 42 | /** 43 | * @return the os version 44 | */ 45 | @JsonProperty 46 | public String getVersion() { 47 | return this.version; 48 | } 49 | 50 | /** 51 | * @return the os architecture 52 | */ 53 | @JsonProperty 54 | public String getArchitecture() { 55 | return this.architecture; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/environment/ServerDetails.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.environment; 5 | 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 8 | 9 | 10 | 11 | /** 12 | * @author Carl Wilson.

13 | */ 14 | @JacksonXmlRootElement 15 | public class ServerDetails { 16 | private static final ServerDetails INSTANCE = new ServerDetails(); 17 | private final String ipAddress; 18 | private final String hostName; 19 | private final String machAddress; 20 | 21 | private ServerDetails() { 22 | this.ipAddress=Environments.getHostAddress(); 23 | this.hostName=Environments.getHostName(); 24 | this.machAddress=Environments.getMachAddress(); 25 | } 26 | 27 | /** 28 | * @return the server details instance 29 | */ 30 | public static final ServerDetails getInstance() { 31 | return INSTANCE; 32 | } 33 | 34 | /** 35 | * @return the JVM's host's IP address 36 | */ 37 | @JsonProperty 38 | public String getIpAddress() { 39 | return this.ipAddress; 40 | } 41 | 42 | /** 43 | * @return the JVM's host's name 44 | */ 45 | @JsonProperty 46 | public String getHostName() { 47 | return this.hostName; 48 | } 49 | 50 | /** 51 | * @return the JVM's host's MACH address 52 | */ 53 | @JsonProperty 54 | public String getMachAddress() { 55 | return this.machAddress; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author Carl Wilson 6 | * 7 | */ 8 | package org.verapdf.rest; -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/resources/ApiResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.resources; 5 | 6 | import io.swagger.v3.oas.annotations.OpenAPIDefinition; 7 | import io.swagger.v3.oas.annotations.Operation; 8 | import io.swagger.v3.oas.annotations.info.Info; 9 | import io.swagger.v3.oas.annotations.info.License; 10 | import io.swagger.v3.oas.annotations.media.Content; 11 | import io.swagger.v3.oas.annotations.media.Schema; 12 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 13 | import io.swagger.v3.oas.annotations.responses.ApiResponses; 14 | import io.swagger.v3.oas.annotations.servers.Server; 15 | import io.swagger.v3.oas.annotations.tags.Tag; 16 | import org.verapdf.ReleaseDetails; 17 | import org.verapdf.rest.environment.Environment; 18 | import org.verapdf.rest.environment.Environments; 19 | 20 | import javax.ws.rs.GET; 21 | import javax.ws.rs.Path; 22 | import javax.ws.rs.Produces; 23 | import javax.ws.rs.core.MediaType; 24 | 25 | /** 26 | * API wrapper resource, provides routing for child API resources. 27 | * 28 | * @author Carl Wilson. 29 | *

30 | * 31 | */ 32 | @Path("/api") 33 | @Tag(name = "veraPDF") 34 | @OpenAPIDefinition(info = @Info(title = "veraPDF API", description = "A REST service API for veraPDF", version = "V1.29.1", license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0")), servers = { 35 | @Server(url = "https://demo.verapdf.org", description = "default"), 36 | @Server(url = "https://dev.verapdf-rest.duallab.com", description = "dev"), 37 | @Server(url = "http://localhost:8080", description = "local") }) 38 | 39 | public final class ApiResource { 40 | private static ReleaseDetails buildDetails = ReleaseDetails.addDetailsFromResource( 41 | ReleaseDetails.APPLICATION_PROPERTIES_ROOT + "rest." + ReleaseDetails.PROPERTIES_EXT); 42 | 43 | @GET 44 | @Path("/") 45 | @Operation(summary = "Returns the release details of the veraPDF library used by the server.") 46 | @ApiResponses(value = { 47 | @ApiResponse(responseCode = "200", description = "Release details successfully returned.", content = { 48 | @Content(mediaType = "application/json", schema = @Schema(implementation = ReleaseDetails.class)), 49 | @Content(mediaType = "application/xml", schema = @Schema(implementation = ReleaseDetails.class)) }) }) 50 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 51 | public static ReleaseDetails getReleaseDetails() { 52 | return buildDetails; 53 | } 54 | 55 | /** 56 | * @return the server environment information as a 57 | * {@link org.verapdf.rest.environment.Environment}. 58 | */ 59 | @GET 60 | @Path("/info") 61 | @Operation(summary = "Returns relevant server information, such as JDK version, OS, etc.") 62 | @ApiResponses(value = { 63 | @ApiResponse(responseCode = "200", description = "Server environment details successfully returned.", content = { 64 | @Content(mediaType = "application/json", schema = @Schema(implementation = Environment.class)), 65 | @Content(mediaType = "application/xml", schema = @Schema(implementation = Environment.class)) }) }) 66 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 67 | public static Environment getEnvironment() { 68 | return Environments.getEnvironment(); 69 | } 70 | 71 | /** 72 | * @return a new {@link org.verapdf.rest.resources.ProfileResource} 73 | */ 74 | @Path("/profiles") 75 | public static ProfileResource getProfileResource() { 76 | return new ProfileResource(); 77 | } 78 | 79 | /** 80 | * @return a new {@link org.verapdf.rest.resources.ValidateResource} 81 | */ 82 | @Path("/validate") 83 | public static ValidateResource getValidateResource() { 84 | return new ValidateResource(); 85 | } 86 | 87 | /** 88 | * @return a new {@link ByteStreamResource} 89 | */ 90 | @Path("/sha1") 91 | public static ByteStreamResource getBytestreamResource() { 92 | return new ByteStreamResource(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/resources/ByteStreamResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.resources; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | import javax.ws.rs.Consumes; 11 | import javax.ws.rs.GET; 12 | import javax.ws.rs.POST; 13 | import javax.ws.rs.Path; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.MediaType; 16 | 17 | import org.glassfish.jersey.media.multipart.FormDataContentDisposition; 18 | import org.glassfish.jersey.media.multipart.FormDataParam; 19 | import org.openpreservation.bytestreams.ByteStreamId; 20 | import org.openpreservation.bytestreams.ByteStreams; 21 | 22 | import io.swagger.v3.oas.annotations.Operation; 23 | import io.swagger.v3.oas.annotations.Parameter; 24 | import io.swagger.v3.oas.annotations.enums.ParameterStyle; 25 | import io.swagger.v3.oas.annotations.media.Content; 26 | import io.swagger.v3.oas.annotations.media.ExampleObject; 27 | import io.swagger.v3.oas.annotations.media.Schema; 28 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 29 | import io.swagger.v3.oas.annotations.responses.ApiResponses; 30 | 31 | /** 32 | * The REST resource definition for byte stream identification services, these 33 | * are JERSEY REST services and it's the annotations that perform the magic of 34 | * handling content types and serialisation. 35 | * 36 | * @author Carl Wilson. 37 | *

38 | */ 39 | public class ByteStreamResource { 40 | private static final String EMPTY_SHA_XML = "\n" + // 41 | " da39a3ee5e6b4b0d3255bfef95601890afd80709\n" + // 42 | " 0\n" + // 43 | "" + // 44 | ""; 45 | private static final String EMPTY_SHA_JSON = "{\n" + // 46 | " \"hexSHA1\": \"da39a3ee5e6b4b0d3255bfef95601890afd80709\",\n" + // 47 | " \"length\": 0\n" + // 48 | "}"; 49 | private static final String TEST_SHA_XML = "\n" + // 50 | " a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\n" + // 51 | " 4\n" + // 52 | "" + // 53 | ""; 54 | private static final String TEST_SHA_JSON = "{\n" + // 55 | " \"hexSHA1\": \"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\",\n" + // 56 | " \"length\": 4\n" + // 57 | "}"; 58 | 59 | /** 60 | * Default public constructor required by Jersey / Dropwizard 61 | */ 62 | public ByteStreamResource() { 63 | /** Intentionally blank */ 64 | } 65 | 66 | /** 67 | * @param uploadedInputStream 68 | * InputStream for the uploaded file 69 | * @param contentDispositionHeader 70 | * extra info about the uploaded file, currently 71 | * unused. 72 | * @return the {@link org.openpreservation.bytestreams.ByteStreamId} of 73 | * the uploaded file's byte stream serialised according to requested 74 | * content type. 75 | * @throws IOException 76 | */ 77 | @POST 78 | @Operation(summary = "Calculates and returns the SHA1 and length of the uploaded file.") 79 | @ApiResponses(value = { 80 | @ApiResponse(responseCode = "200", description = "OK", content = { 81 | @Content(mediaType = "application/xml", schema = @Schema(implementation = ByteStreamId.class), examples = @ExampleObject(name = "Test file.", value = TEST_SHA_XML, summary = "Test file SHA1 as XML", externalValue = "/examples/test.txt")), 82 | @Content(mediaType = "application/json", schema = @Schema(implementation = ByteStreamId.class), examples = @ExampleObject(name = "Test file.", value = TEST_SHA_JSON, summary = "Test file SHA1 as JSON", externalValue = "/examples/test.txt")), 83 | @Content(mediaType = "text/xml", schema = @Schema(implementation = ByteStreamId.class), examples = @ExampleObject(name = "Test file.", value = TEST_SHA_XML, summary = "Test file SHA1 as XML", externalValue = "/examples/test.txt")) 84 | }), 85 | @ApiResponse(responseCode = "500", description = "Server error when processing the request.") 86 | }) 87 | @Consumes(MediaType.MULTIPART_FORM_DATA) 88 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, 89 | MediaType.TEXT_XML }) 90 | public static ByteStreamId getSha1( 91 | @Parameter(name = "file", schema = @Schema(implementation = File.class), style = ParameterStyle.FORM, description = "File uploaded for SHA1 calculation.") @FormDataParam("file") InputStream uploadedInputStream, 92 | @Parameter(hidden = true) @FormDataParam("file") final FormDataContentDisposition contentDispositionHeader) 93 | throws IOException { 94 | ByteStreamId id = ByteStreams.idFromStream(uploadedInputStream); 95 | uploadedInputStream.close(); 96 | return id;// return 97 | } 98 | 99 | /** 100 | * @return the {@link org.openpreservation.bytestreams} of 101 | * an empty (0 byte) byte stream serialised according to requested 102 | * content type. 103 | */ 104 | @GET 105 | @Path("/null") 106 | @Operation(summary = "Returns the SHA1 and zero length of a null byte stream.") 107 | @ApiResponses(value = { 108 | @ApiResponse(responseCode = "200", description = "OK", content = { 109 | @Content(mediaType = "application/xml", schema = @Schema(implementation = ByteStreamId.class), examples = @ExampleObject(name = "Empty SHA1", value = EMPTY_SHA_XML, summary = "Empty SHA1 as XML")), 110 | @Content(mediaType = "application/json", schema = @Schema(implementation = ByteStreamId.class), examples = @ExampleObject(name = "Empty SHA1", value = EMPTY_SHA_JSON, summary = "Empty SHA1 as JSON")), 111 | @Content(mediaType = "text/xml", schema = @Schema(implementation = ByteStreamId.class), examples = @ExampleObject(name = "Empty SHA1", value = EMPTY_SHA_XML, summary = "Empty SHA1 as XML")) 112 | }) }) 113 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, 114 | MediaType.TEXT_XML }) 115 | public static ByteStreamId getEmptySha1() { 116 | return ByteStreams.nullByteStreamId(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/resources/HomePageResource.java: -------------------------------------------------------------------------------- 1 | package org.verapdf.rest.resources; 2 | 3 | import io.swagger.v3.oas.annotations.Hidden; 4 | import org.verapdf.rest.views.RestClientView; 5 | 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.Produces; 9 | import javax.ws.rs.core.MediaType; 10 | 11 | /** 12 | * @author Carl Wilson 13 | * carlwilson AT github 14 | * @version 0.1 Created 3 Sep 2016:17:00:32 15 | */ 16 | @Hidden 17 | @Path("/") 18 | public class HomePageResource { 19 | 20 | /** 21 | * @return a new 22 | * {@link org.verapdf.rest.views.RestClientView} for 23 | * the home page. 24 | */ 25 | @GET 26 | @Produces({ MediaType.TEXT_HTML }) 27 | public static RestClientView client() { 28 | return new RestClientView(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/resources/ProfileResource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.resources; 5 | 6 | import java.util.*; 7 | 8 | import javax.inject.Singleton; 9 | import javax.ws.rs.*; 10 | import javax.ws.rs.core.MediaType; 11 | 12 | import org.verapdf.pdfa.flavours.PDFAFlavour; 13 | import org.verapdf.pdfa.validation.profiles.ProfileDetails; 14 | import org.verapdf.pdfa.validation.profiles.ProfileDirectory; 15 | import org.verapdf.pdfa.validation.profiles.Profiles; 16 | import org.verapdf.pdfa.validation.profiles.Rule; 17 | import org.verapdf.pdfa.validation.profiles.RuleId; 18 | import org.verapdf.pdfa.validation.profiles.ValidationProfile; 19 | 20 | import io.swagger.v3.oas.annotations.Operation; 21 | import io.swagger.v3.oas.annotations.Parameter; 22 | import io.swagger.v3.oas.annotations.media.ArraySchema; 23 | import io.swagger.v3.oas.annotations.media.Content; 24 | import io.swagger.v3.oas.annotations.media.Schema; 25 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 26 | import io.swagger.v3.oas.annotations.responses.ApiResponses; 27 | 28 | /** 29 | * @author Carl Wilson 30 | */ 31 | @Singleton 32 | public class ProfileResource { 33 | 34 | public static final String PROFILE_ID_PARAMETER_DESCRIPTION = "The String ID of the Validation profile " + 35 | "(1b, 1a, 2b, 2a, 2u, 3b, 3a, 3u, 4, 4e, 4f, ua1, ua2, wt1r or wt1a)"; 36 | 37 | public static final String PROFILE_NOT_FOUND_MESSAGE = "The requested profile was not found"; 38 | private static final ProfileDirectory DIRECTORY = Profiles.getVeraProfileDirectory(); 39 | private static final Set DETAILS = new HashSet<>(); 40 | 41 | static { 42 | for (ValidationProfile profile : DIRECTORY.getValidationProfiles()) { 43 | DETAILS.add(profile.getDetails()); 44 | } 45 | } 46 | 47 | /** 48 | * @return the set of validation profile details 49 | */ 50 | @GET 51 | @Operation(summary = "Returns the set of validation profile details for profiles supported by the server.") 52 | @ApiResponses(value = { 53 | @ApiResponse(responseCode = "200", description = "A profile list was returned successfully", content = { 54 | @Content(mediaType = "application/json", schema = @Schema(implementation = ProfileDetails.class)), 55 | @Content(mediaType = "application/xml", schema = @Schema(implementation = ProfileDetails.class)) }) }) 56 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 57 | public static Set getProfileDetails() { 58 | return DETAILS; 59 | } 60 | 61 | /** 62 | * @return the Set of Validation Profile IDs 63 | */ 64 | @GET 65 | @Path("/ids") 66 | @Operation(summary = "Gets the set of IDs for the supported validation profiles.") 67 | @ApiResponses(value = { 68 | @ApiResponse(responseCode = "200", description = "The set of validation profile IDs was returned successfully.", content = { 69 | @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = String.class))), 70 | @Content(mediaType = "application/xml", array = @ArraySchema(schema = @Schema(implementation = String.class))) }) }) 71 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 72 | public static Set getProfileIds() { 73 | return DIRECTORY.getValidationProfileIds(); 74 | } 75 | 76 | /** 77 | * @return the Set of Flavours 78 | */ 79 | @GET 80 | @Path("/flavours") 81 | @Operation(summary = "Returns the set of supported PDF/A and PDF/UA flavours supported by the server.") 82 | @ApiResponses(value = { 83 | @ApiResponse(responseCode = "200", description = "The set of supported flavours was returned successfully.", content = { 84 | @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = PDFAFlavour.class))), 85 | @Content(mediaType = "application/xml", array = @ArraySchema(schema = @Schema(implementation = PDFAFlavour.class))) }) }) 86 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 87 | public static Set getFlavours() { 88 | return DIRECTORY.getPDFAFlavours(); 89 | } 90 | 91 | /** 92 | * @param profileId 93 | * the String id of the Validation profile 94 | * @return a validation profile selected by id 95 | */ 96 | @GET 97 | @Path("/{profileId}") 98 | @Operation(summary = "Returns the validation profile selected by id (validation flavour)") 99 | @ApiResponses(value = { 100 | @ApiResponse(responseCode = "200", description = "The requested profile was found and returned.", content = { 101 | @Content(mediaType = "application/json", schema = @Schema(implementation = ValidationProfile.class)), 102 | @Content(mediaType = "application/xml", schema = @Schema(implementation = ValidationProfile.class)) }), 103 | @ApiResponse(responseCode = "404", description = PROFILE_NOT_FOUND_MESSAGE, content = { 104 | @Content(mediaType = "application/json"), 105 | @Content(mediaType = "application/xml") 106 | }) }) 107 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 108 | public static ValidationProfile getProfile( 109 | @Parameter(description = PROFILE_ID_PARAMETER_DESCRIPTION) @PathParam("profileId") String profileId) { 110 | try { 111 | return DIRECTORY.getValidationProfileById(profileId); 112 | } catch (NoSuchElementException e) { 113 | throw new NotFoundException(PROFILE_NOT_FOUND_MESSAGE); 114 | } 115 | } 116 | 117 | /** 118 | * @param profileId 119 | * the String id of the Validation profile 120 | * @return the {@link java.util.Set} of 121 | * {@link org.verapdf.pdfa.validation.profiles.RuleId}s for the selected 122 | * Validation Profile 123 | */ 124 | @GET 125 | @Path("/{profileId}/ruleids") 126 | @Operation(summary = "Returns the full set of rule IDs for the selected validation profiles.") 127 | @ApiResponses(value = { 128 | @ApiResponse(responseCode = "200", description = "OK", content = { 129 | @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = RuleId.class))), 130 | @Content(mediaType = "application/xml", array = @ArraySchema(schema = @Schema(implementation = RuleId.class))) }), 131 | @ApiResponse(responseCode = "404", description = PROFILE_NOT_FOUND_MESSAGE, content = { 132 | @Content(mediaType = "application/json"), 133 | @Content(mediaType = "application/xml") 134 | }) }) 135 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 136 | public static Set getProfileRules( 137 | @Parameter(description = PROFILE_ID_PARAMETER_DESCRIPTION) @PathParam("profileId") String profileId) { 138 | SortedSet ids = new TreeSet<>(new Profiles.RuleIdComparator()); 139 | try { 140 | for (Rule rule : DIRECTORY.getValidationProfileById(profileId).getRules()) { 141 | ids.add(rule.getRuleId()); 142 | } 143 | } catch (NoSuchElementException e) { 144 | throw new NotFoundException(PROFILE_NOT_FOUND_MESSAGE); 145 | } 146 | return ids; 147 | } 148 | 149 | /** 150 | * @param profileId 151 | * the String id of the Validation profile 152 | * @param clause 153 | * a {@link java.lang.String} identifying the profile clause to 154 | * return the Rules for 155 | * @return the {@link java.util.Set} of 156 | * {@link org.verapdf.pdfa.validation.profiles.Rule}s for the selected 157 | * profile and clause 158 | */ 159 | @GET 160 | @Path("/{profileId}/{clause}") 161 | @Operation(summary = "Gets the set of rules for the selected profile and clause") 162 | @ApiResponses(value = { 163 | @ApiResponse(responseCode = "200", description = "OK", content = { 164 | @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = Rule.class))), 165 | @Content(mediaType = "application/xml", array = @ArraySchema(schema = @Schema(implementation = Rule.class))) }), 166 | @ApiResponse(responseCode = "404", description = PROFILE_NOT_FOUND_MESSAGE, content = { 167 | @Content(mediaType = "application/json"), 168 | @Content(mediaType = "application/xml") 169 | }) }) 170 | @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) 171 | public static Set getRulesForClause(@Parameter(description = PROFILE_ID_PARAMETER_DESCRIPTION) @PathParam("profileId") String profileId, 172 | @Parameter(description = "A string identifying the profile clause to return the rules for.") @PathParam("clause") String clause) { 173 | SortedSet rules = new TreeSet<>(new Profiles.RuleComparator()); 174 | try { 175 | for (Rule rule : DIRECTORY.getValidationProfileById(profileId).getRules()) { 176 | if (rule.getRuleId().getClause().equalsIgnoreCase(clause)) { 177 | rules.add(rule); 178 | } 179 | } 180 | } catch (NoSuchElementException e) { 181 | throw new NotFoundException(PROFILE_NOT_FOUND_MESSAGE); 182 | } 183 | return rules; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/resources/ValidationExceptionMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.resources; 5 | 6 | import javax.ws.rs.core.MediaType; 7 | import javax.ws.rs.core.Response; 8 | import javax.ws.rs.core.Response.Status; 9 | import javax.ws.rs.ext.ExceptionMapper; 10 | 11 | import org.verapdf.core.ValidationException; 12 | 13 | /** 14 | * @author Carl Wilson. 15 | * 16 | */ 17 | public final class ValidationExceptionMapper implements ExceptionMapper { 18 | 19 | @Override 20 | public Response toResponse(ValidationException exception) { 21 | StringBuilder builder = new StringBuilder(); 22 | builder.append(exception.getMessage()); 23 | if (exception.getStackTrace().length > 0) { 24 | StackTraceElement trace = exception.getStackTrace()[0]; 25 | builder.append(" at " + trace.getClassName() + "." + trace.getMethodName() + "(" + trace.getFileName() + ":" + trace.getLineNumber() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ 26 | } 27 | return Response.status(Status.INTERNAL_SERVER_ERROR).entity(builder.toString()).type(MediaType.TEXT_PLAIN).build(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/verapdf/rest/views/RestClientView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.verapdf.rest.views; 5 | 6 | import io.dropwizard.views.View; 7 | 8 | /** 9 | * @author Carl Wilson 10 | * carlwilson AT github 11 | * 12 | * @version 0.1 13 | * 14 | * Created 3 Sep 2016:17:05:03 15 | */ 16 | public class RestClientView extends View { 17 | 18 | /** 19 | * Default constructor, simply calls super constructor with mustache template 20 | */ 21 | public RestClientView() { 22 | super("restclient.mustache"); //$NON-NLS-1$ 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/assets/css/verapdf.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-bottom: 30px; 3 | } 4 | 5 | .container .jumbotron { 6 | color: white; 7 | background-color: #D0404F; 8 | /* This is mostly intended for prototyping; please download the pattern and re-host for production environments. Thank you! */ 9 | background-image: url("/img/vera-pages.jpg"); 10 | padding: 0; 11 | } 12 | 13 | .jumbotron .container { 14 | padding: 20px; 15 | padding-left: 60px; 16 | } 17 | 18 | .jumbotron img { 19 | padding: 0; 20 | float: left; 21 | width: 200px; 22 | margin: 0 10px 0 0; 23 | } 24 | 25 | .shadowed { 26 | filter: "progid:DXImageTransform.Microsoft.Dropshadow(OffX=12, OffY=12, Color='#444')"; 27 | filter: url(#drop-shadow); 28 | -webkit-filter: drop-shadow(12px 12px 7px rgba(0,0,0,0.5)); 29 | filter: drop-shadow(12px 12px 7px rgba(0,0,0,0.5)); 30 | } 31 | 32 | .radialmask { 33 | background: -moz-radial-gradient(center, ellipse cover, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.9) 20%, rgba(0,0,0,0.05) 100%); /* FF3.6+ */ 34 | background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(0,0,0,0.9)), color-stop(20%,rgba(0,0,0,0.9)), color-stop(100%,rgba(0,0,0,0.05))); /* Chrome,Safari4+ */ 35 | background: -webkit-radial-gradient(center, ellipse cover, rgba(0,0,0,0.9) 0%,rgba(0,0,0,0.9) 20%,rgba(0,0,0,0.05) 100%); /* Chrome10+,Safari5.1+ */ 36 | background: -o-radial-gradient(center, ellipse cover, rgba(0,0,0,0.9) 0%,rgba(0,0,0,0.9) 20%,rgba(0,0,0,0.05) 100%); /* Opera 12+ */ 37 | background: -ms-radial-gradient(center, ellipse cover, rgba(0,0,0,0.9) 0%,rgba(0,0,0,0.9) 20%,rgba(0,0,0,0.05) 100%); /* IE10+ */ 38 | background: radial-gradient(ellipse at center, rgba(0,0,0,0.9) 0%,rgba(0,0,0,0.9) 20%,rgba(0,0,0,0.05) 100%); /* W3C */ 39 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e6000000', endColorstr='#0d000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ 40 | } 41 | 42 | .jumbotron h1 .jumbotron p { 43 | color: white; 44 | position: relative; 45 | left: 10px; 46 | } 47 | 48 | .theme-showcase > p > .btn { 49 | margin: 5px 0; 50 | } 51 | .stepwizard-step p { 52 | margin-top: 10px; 53 | } 54 | 55 | .stepwizard-row { 56 | display: table-row; 57 | } 58 | 59 | .stepwizard { 60 | display: table; 61 | width: 100%; 62 | position: relative; 63 | } 64 | 65 | .stepwizard-step button[disabled] { 66 | opacity: 1 !important; 67 | filter: alpha(opacity=100) !important; 68 | } 69 | 70 | .stepwizard-row:before { 71 | top: 14px; 72 | bottom: 0; 73 | position: absolute; 74 | content: " "; 75 | width: 100%; 76 | height: 1px; 77 | background-color: #ccc; 78 | z-order: 0; 79 | 80 | } 81 | 82 | .stepwizard-step { 83 | display: table-cell; 84 | text-align: center; 85 | position: relative; 86 | } 87 | 88 | .btn-circle { 89 | width: 30px; 90 | height: 30px; 91 | text-align: center; 92 | padding: 6px 0; 93 | font-size: 12px; 94 | line-height: 1.428571429; 95 | border-radius: 15px; 96 | } 97 | 98 | .btn-file { 99 | position: relative; 100 | overflow: hidden; 101 | } 102 | .btn-file input[type=file] { 103 | position: absolute; 104 | top: 0; 105 | right: 0; 106 | min-width: 100%; 107 | min-height: 100%; 108 | font-size: 100px; 109 | text-align: right; 110 | filter: alpha(opacity=0); 111 | opacity: 0; 112 | background: red; 113 | cursor: inherit; 114 | display: block; 115 | } 116 | input[readonly] { 117 | background-color: white !important; 118 | cursor: text !important; 119 | } 120 | .voffset { margin-top: 2px; } 121 | .voffset1 { margin-top: 5px; } 122 | .voffset2 { margin-top: 10px; } 123 | .voffset3 { margin-top: 15px; } 124 | .voffset4 { margin-top: 30px; } 125 | .voffset5 { margin-top: 40px; } 126 | .voffset6 { margin-top: 60px; } 127 | .voffset7 { margin-top: 80px; } 128 | .voffset8 { margin-top: 100px; } 129 | .voffset9 { margin-top: 150px; } 130 | 131 | .form-wrapper { 132 | margin-bottom: 15px; 133 | } 134 | 135 | .container .btn-lg { 136 | padding: 6px 12px; 137 | font-size: 16px; 138 | } 139 | 140 | #results .alert { 141 | margin-top: 20px; 142 | } 143 | 144 | #validate .list-group { 145 | box-shadow: none; 146 | } 147 | 148 | table { 149 | width: 100%; 150 | } 151 | 152 | .error-form-data { 153 | color: red; 154 | } 155 | 156 | #configure-validator-header, #result-details { 157 | overflow-wrap: anywhere; 158 | font-weight: 600; 159 | font-size: 0.95em; 160 | } 161 | 162 | .header-for-name { 163 | display: flex; 164 | align-items: flex-start; 165 | } 166 | 167 | .header-for-name h1{ 168 | margin-top: 0; 169 | margin-bottom: 8px; 170 | } 171 | 172 | @media screen and (max-width: 1200px) { 173 | .header-for-name { 174 | position: relative; 175 | } 176 | 177 | .header-for-name h1 { 178 | margin-bottom: 40px; 179 | } 180 | 181 | .header-for-name p { 182 | position: absolute; 183 | left: 0; 184 | bottom: -20px; 185 | } 186 | } 187 | 188 | @media screen and (max-width: 1000px) { 189 | .header-for-name { 190 | margin-bottom: 10px; 191 | } 192 | 193 | .header-for-name p { 194 | bottom: -35px; 195 | } 196 | 197 | .header-for-name h1 { 198 | margin-bottom: 50px; 199 | } 200 | } 201 | 202 | @media screen and (max-width: 760px) { 203 | .header-for-name h1 { 204 | margin-bottom: 80px; 205 | } 206 | 207 | .header-for-name p { 208 | bottom: -35px; 209 | } 210 | } 211 | 212 | @media screen and (max-width: 500px) { 213 | .jumbotron .container { 214 | padding-left: 20px; 215 | } 216 | } 217 | 218 | @media screen and (max-width: 450px) { 219 | .header-for-name { 220 | flex-direction: column; 221 | } 222 | 223 | .header-for-name h1 { 224 | margin-top: 10px; 225 | } 226 | } 227 | 228 | @media screen and (max-width: 300px) { 229 | .header-for-name h1 { 230 | margin-bottom: 100px; 231 | } 232 | } -------------------------------------------------------------------------------- /src/main/resources/assets/img/vera-pages.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/src/main/resources/assets/img/vera-pages.jpg -------------------------------------------------------------------------------- /src/main/resources/assets/img/veraPDF-logo-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/src/main/resources/assets/img/veraPDF-logo-400.png -------------------------------------------------------------------------------- /src/main/resources/assets/js/jquery.bootstrap.wizard.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery twitter bootstrap wizard plugin 3 | * Examples and documentation at: http://github.com/VinceG/twitter-bootstrap-wizard 4 | * version 1.0 5 | * Requires jQuery v1.3.2 or later 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * Authors: Vadim Vincent Gabriel (http://vadimg.com), Jason Gill (www.gilluminate.com) 10 | */ 11 | ;(function($) { 12 | var bootstrapWizardCreate = function(element, options) { 13 | var element = $(element); 14 | var obj = this; 15 | 16 | // Merge options with defaults 17 | var $settings = $.extend({}, $.fn.bootstrapWizard.defaults, options); 18 | var $activeTab = null; 19 | var $navigation = null; 20 | 21 | this.rebindClick = function(selector, fn) 22 | { 23 | selector.unbind('click', fn).bind('click', fn); 24 | } 25 | 26 | this.fixNavigationButtons = function() { 27 | // Get the current active tab 28 | if(!$activeTab.length) { 29 | // Select first one 30 | $navigation.find('a:first').tab('show'); 31 | $activeTab = $navigation.find('li:first'); 32 | } 33 | 34 | // See if we're currently in the first/last then disable the previous and last buttons 35 | $($settings.previousSelector, element).toggleClass('disabled', (obj.firstIndex() >= obj.currentIndex())); 36 | $($settings.nextSelector, element).toggleClass('disabled', (obj.currentIndex() >= obj.navigationLength())); 37 | 38 | // We are unbinding and rebinding to ensure single firing and no double-click errors 39 | obj.rebindClick($($settings.nextSelector, element), obj.next); 40 | obj.rebindClick($($settings.previousSelector, element), obj.previous); 41 | obj.rebindClick($($settings.lastSelector, element), obj.last); 42 | obj.rebindClick($($settings.firstSelector, element), obj.first); 43 | 44 | if($settings.onTabShow && typeof $settings.onTabShow === 'function' && $settings.onTabShow($activeTab, $navigation, obj.currentIndex())===false){ 45 | return false; 46 | } 47 | }; 48 | 49 | this.next = function(e) { 50 | 51 | // If we clicked the last then dont activate this 52 | if(element.hasClass('last')) { 53 | return false; 54 | } 55 | 56 | if($settings.onNext && typeof $settings.onNext === 'function' && $settings.onNext($activeTab, $navigation, obj.nextIndex())===false){ 57 | return false; 58 | } 59 | 60 | // Did we click the last button 61 | $index = obj.nextIndex(); 62 | if($index > obj.navigationLength()) { 63 | } else { 64 | $navigation.find('li:eq('+$index+') a').tab('show'); 65 | } 66 | }; 67 | 68 | this.previous = function(e) { 69 | 70 | // If we clicked the first then dont activate this 71 | if(element.hasClass('first')) { 72 | return false; 73 | } 74 | 75 | if($settings.onPrevious && typeof $settings.onPrevious === 'function' && $settings.onPrevious($activeTab, $navigation, obj.previousIndex())===false){ 76 | return false; 77 | } 78 | 79 | $index = obj.previousIndex(); 80 | if($index < 0) { 81 | } else { 82 | $navigation.find('li:eq('+$index+') a').tab('show'); 83 | } 84 | }; 85 | 86 | this.first = function(e) { 87 | if($settings.onFirst && typeof $settings.onFirst === 'function' && $settings.onFirst($activeTab, $navigation, obj.firstIndex())===false){ 88 | return false; 89 | } 90 | 91 | // If the element is disabled then we won't do anything 92 | if(element.hasClass('disabled')) { 93 | return false; 94 | } 95 | $navigation.find('li:eq(0) a').tab('show'); 96 | 97 | }; 98 | this.last = function(e) { 99 | if($settings.onLast && typeof $settings.onLast === 'function' && $settings.onLast($activeTab, $navigation, obj.lastIndex())===false){ 100 | return false; 101 | } 102 | 103 | // If the element is disabled then we won't do anything 104 | if(element.hasClass('disabled')) { 105 | return false; 106 | } 107 | $navigation.find('li:eq('+obj.navigationLength()+') a').tab('show'); 108 | }; 109 | this.currentIndex = function() { 110 | return $navigation.find('li').index($activeTab); 111 | }; 112 | this.firstIndex = function() { 113 | return 0; 114 | }; 115 | this.lastIndex = function() { 116 | return obj.navigationLength(); 117 | }; 118 | this.getIndex = function(e) { 119 | return $navigation.find('li').index(e); 120 | }; 121 | this.nextIndex = function() { 122 | return $navigation.find('li').index($activeTab) + 1; 123 | }; 124 | this.previousIndex = function() { 125 | return $navigation.find('li').index($activeTab) - 1; 126 | }; 127 | this.navigationLength = function() { 128 | return $navigation.find('li').length - 1; 129 | }; 130 | this.activeTab = function() { 131 | return $activeTab; 132 | }; 133 | this.nextTab = function() { 134 | return $navigation.find('li:eq('+(obj.currentIndex()+1)+')').length ? $navigation.find('li:eq('+(obj.currentIndex()+1)+')') : null; 135 | }; 136 | this.previousTab = function() { 137 | if(obj.currentIndex() <= 0) { 138 | return null; 139 | } 140 | return $navigation.find('li:eq('+parseInt(obj.currentIndex()-1)+')'); 141 | }; 142 | this.show = function(index) { 143 | return element.find('li:eq(' + index + ') a').tab('show'); 144 | }; 145 | this.disable = function(index) { 146 | $navigation.find('li:eq('+index+')').addClass('disabled'); 147 | }; 148 | this.enable = function(index) { 149 | $navigation.find('li:eq('+index+')').removeClass('disabled'); 150 | }; 151 | this.hide = function(index) { 152 | $navigation.find('li:eq('+index+')').hide(); 153 | }; 154 | this.display = function(index) { 155 | $navigation.find('li:eq('+index+')').show(); 156 | }; 157 | this.remove = function(args) { 158 | var $index = args[0]; 159 | var $removeTabPane = typeof args[1] != 'undefined' ? args[1] : false; 160 | var $item = $navigation.find('li:eq('+$index+')'); 161 | 162 | // Remove the tab pane first if needed 163 | if($removeTabPane) { 164 | var $href = $item.find('a').attr('href'); 165 | $($href).remove(); 166 | } 167 | 168 | // Remove menu item 169 | $item.remove(); 170 | }; 171 | 172 | $navigation = element.find('ul:first', element); 173 | $activeTab = $navigation.find('li.active', element); 174 | 175 | if(!$navigation.hasClass($settings.tabClass)) { 176 | $navigation.addClass($settings.tabClass); 177 | } 178 | 179 | // Load onInit 180 | if($settings.onInit && typeof $settings.onInit === 'function'){ 181 | $settings.onInit($activeTab, $navigation, 0); 182 | } 183 | 184 | // Load onShow 185 | if($settings.onShow && typeof $settings.onShow === 'function'){ 186 | $settings.onShow($activeTab, $navigation, obj.nextIndex()); 187 | } 188 | 189 | // Work the next/previous buttons 190 | obj.fixNavigationButtons(); 191 | 192 | $('a[data-toggle="tab"]', $navigation).on('click', function (e) { 193 | // Get the index of the clicked tab 194 | var clickedIndex = $navigation.find('li').index($(e.currentTarget).parent('li')); 195 | if($settings.onTabClick && typeof $settings.onTabClick === 'function' && $settings.onTabClick($activeTab, $navigation, obj.currentIndex(), clickedIndex)===false){ 196 | return false; 197 | } 198 | }); 199 | 200 | $('a[data-toggle="tab"]', $navigation).on('shown', function (e) { // use shown instead of show to help prevent double firing 201 | $element = $(e.target).parent(); 202 | var nextTab = $navigation.find('li').index($element); 203 | 204 | // If it's disabled then do not change 205 | if($element.hasClass('disabled')) { 206 | return false; 207 | } 208 | 209 | if($settings.onTabChange && typeof $settings.onTabChange === 'function' && $settings.onTabChange($activeTab, $navigation, obj.currentIndex(), nextTab)===false){ 210 | return false; 211 | } 212 | 213 | $activeTab = $element; // activated tab 214 | obj.fixNavigationButtons(); 215 | }); 216 | }; 217 | $.fn.bootstrapWizard = function(options) { 218 | //expose methods 219 | if (typeof options == 'string') { 220 | var args = Array.prototype.slice.call(arguments, 1) 221 | if(args.length === 1) { 222 | args.toString(); 223 | } 224 | return this.data('bootstrapWizard')[options](args); 225 | } 226 | return this.each(function(index){ 227 | var element = $(this); 228 | // Return early if this element already has a plugin instance 229 | if (element.data('bootstrapWizard')) return; 230 | // pass options to plugin constructor 231 | var wizard = new bootstrapWizardCreate(element, options); 232 | // Store plugin object in this element's data 233 | element.data('bootstrapWizard', wizard); 234 | }); 235 | }; 236 | 237 | // expose options 238 | $.fn.bootstrapWizard.defaults = { 239 | tabClass: 'nav nav-pills', 240 | nextSelector: '.wizard li.next', 241 | previousSelector: '.wizard li.previous', 242 | firstSelector: '.wizard li.first', 243 | lastSelector: '.wizard li.last', 244 | onShow: null, 245 | onInit: null, 246 | onNext: null, 247 | onPrevious: null, 248 | onLast: null, 249 | onFirst: null, 250 | onTabChange: null, 251 | onTabClick: null, 252 | onTabShow: null 253 | }; 254 | 255 | })(jQuery); 256 | -------------------------------------------------------------------------------- /src/main/resources/assets/js/vera.results.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @namespace JS Encapsulation of information required to render validation results. 3 | */ 4 | var pdfaValidator = { 5 | // validation result 6 | result: null, 7 | status: null, 8 | details: null, 9 | release: null, 10 | getDetails: function (callback, contentType = "json") { 11 | $.ajax({ 12 | url: '/api/validate/details/', 13 | type: 'GET', 14 | success: function (data, textStatus, jqXHR) { 15 | console.log(jqXHR) 16 | console.log(data) 17 | pdfaValidator.details = data 18 | callback() 19 | } 20 | }) 21 | }, 22 | getRelease: function (callback, contentType = "json") { 23 | $.ajax({ 24 | url: "/api/", 25 | type: "GET", 26 | success: function (data, textStatus, jqXHR) { 27 | console.log(jqXHR) 28 | console.log(data) 29 | pdfaValidator.release = data; 30 | callback() 31 | } 32 | }) 33 | }, 34 | validate: function (formData, flavour, callback, contentType = "json", fileSize) { 35 | $.ajax({ 36 | beforeSend(xhrObj) { 37 | let headerpt1 = (contentType === "html") ? "text/" : "application/"; 38 | xhrObj.setRequestHeader("Accept", headerpt1 + contentType); 39 | }, 40 | url: "/api/validate/" + flavour + "/", 41 | type: 'POST', 42 | data: formData, 43 | dataType: contentType, 44 | contentType: false, 45 | processData: false, 46 | headers: { 47 | "X-File-Size": fileSize 48 | }, 49 | success: function (data, textStatus, jqXHR) { 50 | pdfaValidator.result = jqXHR.responseText 51 | callback() 52 | }, 53 | error: function (jqXHR, textStatus, errorThrown) { 54 | $('#results').empty() 55 | var template = $('#error-template').html() 56 | Mustache.parse(template) // optional, speeds up future uses 57 | var rendered = Mustache.render(template, jqXHR) 58 | $('#results').html(rendered) 59 | console.log('Validation Error: ' + textStatus + errorThrown) 60 | console.log(jqXHR) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/assets/js/verapdf.js: -------------------------------------------------------------------------------- 1 | var errorOnLoadType = false; 2 | $(document).on('change', '.btn-file :file', function () { 3 | let input = $(this) 4 | let numFiles = input.get(0).files ? input.get(0).files.length : 1 5 | let label = input.val().replace(/\\/g, '/').replace(/.*\//, '') 6 | let rusha = new Rusha() 7 | let file = input.get(0).files[0] 8 | let reader = new FileReader() 9 | reader.onload = function (e) { 10 | var selectedFile = $('#fileInput')[0].files[0]; 11 | var allowedTypes = ['application/pdf']; 12 | 13 | if (!allowedTypes.includes(selectedFile.type)) { 14 | $('#filename').val('Invalid file type. Please upload a PDF file.'); 15 | $('#sha1Hex').val(''); 16 | $('#filename').addClass("error-form-data"); 17 | errorOnLoadType = true; 18 | $('.nextBtn').hide(); 19 | }else{ 20 | if($("#filename").hasClass( "error-form-data" )){ 21 | $('#filename').removeClass("error-form-data"); 22 | } 23 | let rawData = reader.result 24 | let digest = rusha.digest(rawData) 25 | input.trigger('fileselect', [numFiles, label, digest]) 26 | 27 | let logInfo = numFiles > 1 ? numFiles + ' files selected' : label 28 | if (input.length) { 29 | $('#filename').val(logInfo) 30 | } else { 31 | if (logInfo) alert(logInfo) 32 | } 33 | $('#sha1Hex').val(digest) 34 | errorOnLoadType = false; 35 | $('.nextBtn').show(); 36 | } 37 | } 38 | $('a[href="#validate"]').attr("disabled","disabled"); 39 | $('#configure-validator-header').text($('#fileInput')[0].files[0].name); 40 | reader.readAsBinaryString(file) 41 | }) 42 | 43 | $(document).ready(function () { 44 | pdfaValidator.getDetails(function () { 45 | let footer = $('

').text(pdfaValidator.details.description + ' v' + pdfaValidator.details.version) 46 | $('#footer').append(footer) 47 | }) 48 | pdfaValidator.getRelease(function () { 49 | let footer = $('

').text(pdfaValidator.release.id + ' v' + pdfaValidator.release.version + ' ' + new Date(pdfaValidator.release.buildDate).toLocaleString()) 50 | $('#footer').append(footer) 51 | }) 52 | $('.btn-file :file').on('fileselect', function (event, numFiles, label, digest) { 53 | let input = $(this).parents('.input-group').find(':text') 54 | let log = numFiles > 1 ? numFiles + ' files selected' : label 55 | 56 | if (input.length) { 57 | input.val(log) 58 | if (!errorOnLoadType) $('.nextBtn').show(); 59 | } else { 60 | if (log) alert(log) 61 | } 62 | $('#sha1Hex').val(digest) 63 | }) 64 | }) 65 | 66 | $(document).ready(function () { 67 | if($('#fileInput')[0].files.length === 0 ){ 68 | $('.nextBtn').hide(); 69 | } 70 | let navListItems = $('div.setup-panel div a') 71 | let allWells = $('.setup-content') 72 | var allPreviousBtn = $('.previousBtn') 73 | var allNextBtn = $('.nextBtn') 74 | 75 | allWells.hide() 76 | 77 | navListItems.click(function (e) { 78 | e.preventDefault() 79 | var $target = $($(this).attr('href')) 80 | var $item = $(this) 81 | 82 | if (!$item.hasClass('disabled')) { 83 | navListItems.removeClass('btn-primary').addClass('btn-default') 84 | $item.addClass('btn-primary') 85 | allWells.hide() 86 | $target.show() 87 | $target.find('input:eq(0)').focus() 88 | } 89 | $('#download-results-btn').hide(); 90 | }) 91 | 92 | allNextBtn.click(function () { 93 | var curStep = $(this).closest('.setup-content') 94 | var curStepBtn = curStep.attr('id') 95 | var nextStepWizard = $('div.setup-panel div a[href="#' + curStepBtn + '"]').parent().next().children('a') 96 | var curInputs = curStep.find("input[type='text'],input[type='url']") 97 | var isValid = true 98 | $('.form-group').removeClass('has-error') 99 | for (var i = 0; i < curInputs.length; i++) { 100 | if (!curInputs[i].validity.valid) { 101 | isValid = false 102 | $(curInputs[i]).closest('.form-group').addClass('has-error') 103 | } 104 | } 105 | 106 | if (isValid) { 107 | nextStepWizard.removeAttr('disabled') 108 | nextStepWizard[0].click(); 109 | } 110 | if (curStepBtn === 'configure') { 111 | callVeraPdfService() 112 | } 113 | }) 114 | 115 | allPreviousBtn.click(function () { 116 | var curStep = $(this).closest('.setup-content') 117 | var curStepBtn = curStep.attr('id') 118 | var nextStepWizard = $('div.setup-panel div a[href="#' + curStepBtn + '"]').parent().prev().children('a') 119 | var curInputs = curStep.find("input[type='text'],input[type='url']") 120 | var isValid = true 121 | $('.form-group').removeClass('has-error') 122 | for (var i = 0; i < curInputs.length; i++) { 123 | if (!curInputs[i].validity.valid) { 124 | isValid = false 125 | $(curInputs[i]).closest('.form-group').addClass('has-error') 126 | } 127 | } 128 | 129 | if (isValid) { 130 | nextStepWizard.removeAttr('disabled') 131 | nextStepWizard[0].click(); 132 | } 133 | }) 134 | 135 | $('#download-results-btn').hide(); 136 | 137 | $('div.setup-panel div a.btn-primary').trigger('click') 138 | }) 139 | 140 | // Default profile flavour to use for validation 141 | var flavour = 'auto' 142 | 143 | function changeFlavour (newFlavour) { 144 | flavour = newFlavour 145 | } 146 | 147 | var outputFormat = 'html' 148 | 149 | function changeOutputFormat (newFormat) { 150 | outputFormat = newFormat; 151 | } 152 | 153 | function callVeraPdfService () { 154 | var formData = new FormData($('form')[0]) 155 | $.when($('#results').empty()).then(addFileConfigurationToResult()); 156 | var spinHtml = $('#spinner-template').html() 157 | $('#results').html(spinHtml) 158 | var selectedFile = $('#fileInput')[0].files[0]; 159 | pdfaValidator.validate(formData, flavour, function () { 160 | $.when(renderResult()).done(showDownloadBtn()); 161 | }, outputFormat, selectedFile.size) 162 | } 163 | 164 | function addFileConfigurationToResult () { 165 | $("#result-details").text($('#fileInput')[0].files[0].name); 166 | } 167 | 168 | function showDownloadBtn () { 169 | $('#download-results-btn').show(); 170 | } 171 | 172 | function renderResult () { 173 | $('#results').empty() 174 | if (outputFormat === 'html') { 175 | $('#results').html(pdfaValidator.result) 176 | } else { 177 | var preBlock = $('

').text(pdfaValidator.result)
178 |     $('#results').append(preBlock)
179 |   }
180 | }
181 | 
182 | function downloadResult () {
183 |   var texts = pdfaValidator.result;
184 |   var hidden_a = document.createElement('a');
185 |   switch(outputFormat) {
186 |     case 'html':  hidden_a.setAttribute('href', 'data:text/html;charset=utf-8,'+ encodeURIComponent(texts));
187 |       break;
188 |     case 'xml': hidden_a.setAttribute('href', 'data:application/xml;charset=utf-8,'+ encodeURIComponent(texts));
189 |       break;
190 |     case 'json': hidden_a.setAttribute('href', 'data:application/json;charset=utf-8,'+ encodeURIComponent(texts));
191 |       break;
192 |     default: hidden_a.setAttribute('href', 'data:text/plain;charset=utf-8,'+ encodeURIComponent(texts));
193 |       break;
194 |   }
195 | 
196 |   hidden_a.setAttribute('download', "validation_results");
197 |   document.body.appendChild(hidden_a);
198 |   hidden_a.click();
199 | }
200 | 
201 | $(window).on('load', function(){
202 |   $('#filename').val('');
203 |   $('#sha1Hex').val('');
204 |   flavour = 'auto';
205 |   outputFormat = 'html';
206 |   $("#flavour option:selected").prop("selected", false);
207 |   $("#outputFormat option:selected").prop("selected", false);
208 | });


--------------------------------------------------------------------------------
/src/main/resources/org/verapdf/release/rest.properties:
--------------------------------------------------------------------------------
1 | verapdf.project.id=${project.artifactId}
2 | verapdf.release.version=${project.version}
3 | verapdf.release.date=${verapdf.timestamp}
4 | verapdf.date.format=${maven.build.timestamp.format}
5 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/format.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/format/app.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml
11 | 
12 | }
13 | 
14 | teardown() {
15 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml $BATS_TEST_TMPDIR
16 |     cat $BATS_TEST_TMPDIR/app.xml >&3
17 | 
18 |     echo -e "Done ..." >&3
19 | }
20 | 
21 | @test "--format, Chooses output format, format=XML" {
22 | 
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/Mustang_505.pdf" localhost:8080/api/validate/1b -H 'accept: application/xml'
24 |     assert_output --partial "Mustang_505.pdf"
25 | 
26 |     [ "$status" -eq 0 ]
27 | }
28 | 
29 | @test "--format, Chooses output format, the default format=JSON" {
30 | 
31 |     run curl -F "file=@$PROJECT_ROOT/Resources/Mustang_505.pdf" localhost:8080/api/validate/1b
32 |     assert_output --partial '"jobEndStatus" : "normal"'
33 | 
34 |     [ "$status" -eq 0 ]
35 | }
36 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/format/app.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     
4 |     https://github.com/veraPDF/veraPDF-validation-profiles/wiki/
5 |     
6 | 
7 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/type.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/type/app.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml
11 |     docker cp $BATS_TEST_DIRNAME/type/features.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/features.xml
12 |     docker cp $BATS_TEST_DIRNAME/type/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
13 | 
14 | }
15 | 
16 | teardown() {
17 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml $BATS_TEST_TMPDIR
18 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
19 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/features.xml $BATS_TEST_TMPDIR
20 | 
21 |     cat $BATS_TEST_TMPDIR/app.xml >&3
22 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
23 |     cat $BATS_TEST_TMPDIR/features.xml >&3
24 | 
25 |     echo -e "Done ..." >&3
26 | }
27 | 
28 | @test "--type, The default processing model for the GUI, type=VALIDATE_EXTRACT" {
29 | 
30 |     run curl -F "file=@$PROJECT_ROOT/Resources/5-t02-fail-a.pdf" localhost:8080/api/validate/ua1 -H "Accept:text/html"
31 |     assert_output --partial "Information dictionary"
32 |     assert_output --partial "PDF/UA-1 validation profile"
33 | 
34 |     [ "$status" -eq 0 ]
35 | }
36 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/type/app.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     
4 |     https://github.com/veraPDF/veraPDF-validation-profiles/wiki/
5 |     
6 | 
7 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/type/features.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     
4 |         INFORMATION_DICTIONARY
5 |     
6 | 
7 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/type/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/wikiPath.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     sed -i '3 c\    '$BATS_TEST_TMPDIR'' $BATS_TEST_DIRNAME/wikiPath/app.xml
11 |     docker cp $BATS_TEST_DIRNAME/wikiPath/app.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml
12 | 
13 | }
14 | 
15 | teardown() {
16 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml $BATS_TEST_TMPDIR
17 |     cat $BATS_TEST_TMPDIR/app.xml >&3 
18 |     echo -e "Done ..." >&3
19 | }
20 | 
21 | @test "--wikiPath, Defines the base URL used to create reference links in the HTML report, wikiPath=https://baseURL.com/wiki/" {
22 |  
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/Mustang_505.pdf" localhost:8080/api/validate/2b -H "Accept:text/html"
24 |     assert_output --partial "https://baseURL.com/wiki/"
25 | 
26 |     [ "$status" -eq 0 ]
27 | }
28 | 


--------------------------------------------------------------------------------
/tests/Options/config/app/wikiPath/app.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     /tmp/bats-run-7DQP9j/test/3
4 |     https://baseURL.com/wiki/
5 |     
6 | 
7 | 


--------------------------------------------------------------------------------
/tests/Options/config/features/features.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/features/app.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml
11 |     docker cp $BATS_TEST_DIRNAME/features/features.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/features.xml
12 | 
13 | }
14 | 
15 | teardown() {
16 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/app.xml $BATS_TEST_TMPDIR
17 |     cat $BATS_TEST_TMPDIR/app.xml >&3 
18 | 
19 |     echo -e "\nDone ..." >&3
20 | }
21 | 
22 | @test "--features, Extracts and reports PDF features, checking feature=ICCPROFILE" {
23 |  
24 |     run curl -F "file=@$PROJECT_ROOT/Resources/Mustang_505.pdf" localhost:8080/api/validate/1b -H "Accept:text/html"
25 |     assert_output --partial "ICC profiles"
26 | 
27 |     [ "$status" -eq 0 ]
28 | }
29 | 


--------------------------------------------------------------------------------
/tests/Options/config/features/features/app.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     
4 |     https://github.com/veraPDF/veraPDF-validation-profiles/wiki/
5 |     
6 | 
7 | 


--------------------------------------------------------------------------------
/tests/Options/config/features/features/features.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |         ACTION
 5 |         ANNOTATION
 6 |         COLORSPACE
 7 |         DOCUMENT_SECURITY
 8 |         EMBEDDED_FILE
 9 |         EXT_G_STATE
10 |         FONT
11 |         FORM_XOBJECT
12 |         ICCPROFILE
13 |         IMAGE_XOBJECT
14 |         INFORMATION_DICTIONARY
15 |         INTERACTIVE_FORM_FIELDS
16 |         LOW_LEVEL_INFO
17 |         METADATA
18 |         OUTLINES
19 |         OUTPUTINTENT
20 |         PAGE
21 |         PATTERN
22 |         POSTSCRIPT_XOBJECT
23 |         PROPERTIES
24 |         SHADING
25 |         SIGNATURE
26 |     
27 | 
28 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/debug.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | BATS_TEST_TIMEOUT=120  # sec
 3 | 
 4 | setup() {
 5 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 6 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 7 |     _common_setup
 8 | 
 9 |     prepare_fresh_verapdf_config_files
10 | 
11 |     docker cp $BATS_TEST_DIRNAME/debug/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
12 | 
13 | }
14 | 
15 | teardown() {
16 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
17 |     echo -e "\n" >&3
18 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
19 | 
20 |     echo -e "\nDone ..." >&3
21 | }
22 | 
23 | @test "--debug, Outputs all processed file names, debug=true" {
24 | 
25 |     run curl -F "file=@$PROJECT_ROOT/Resources/a_for_debug.pdf" localhost:8080/api/validate/3a -H "Accept:text/html"
26 |     [ "$status" -eq 0 ]
27 | 
28 |     sleep 3 # sleep sec
29 |     run docker logs $DOCKER_CONTAINER -t --since=$test_logs_starting_time
30 |     
31 |     sleep 3 # sleep sec
32 |     echo "$output" >&3
33 |     assert_output --partial "INFO: a_for_debug.pdf"
34 | }
35 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/debug/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/flavour.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/flavour/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
11 | 
12 | }
13 | 
14 | teardown() {
15 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
16 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
17 | 
18 |     echo -e "\nDone ..." >&3
19 | }
20 | 
21 | @test "--flavour, flavour will be applied based on a curl options, flavour=2b" {
22 | 
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/a.pdf" localhost:8080/api/validate/2b  -H 'accept: application/json'
24 |     assert_output --partial '"profileName" : "PDF/A-2B validation profile"'
25 | 
26 |     [ "$status" -eq 0 ]
27 | }
28 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/flavour/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/isLogsEnabled.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/isLogsEnabled/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
11 | 
12 | }
13 | 
14 | teardown() {
15 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
16 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
17 | 
18 |     echo -e "\nDone ..." >&3
19 | }
20 | 
21 | @test "--isLogsEnabled, Add logs to report, isLogsEnabled=true" {
22 | 
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/6.1.3-01-fail-5.pdf" localhost:8080/api/validate/1b -H "Accept:text/html"
24 |     assert_output --partial "WARNING"
25 |     assert_output --partial "SEVERE"
26 | 
27 |     [ "$status" -eq 0 ]
28 | }
29 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/isLogsEnabled/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/loggingLevel.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 | 
11 |     docker cp $BATS_TEST_DIRNAME/loggingLevel/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
12 | 
13 | }
14 | 
15 | teardown() {
16 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
17 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
18 | 
19 |     echo -e "\nDone ..." >&3
20 | }
21 | 
22 | @test "--loggingLevel, Determine the log level, loggingLevel=ALL" {
23 | 
24 |     run curl -F "file=@$PROJECT_ROOT/Resources/orange.pdf" localhost:8080/api/validate/1b -H "Accept:text/html"
25 |     assert_output --partial 'FINE'
26 | 
27 |     [ "$status" -eq 0 ]
28 | }
29 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/loggingLevel/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/maxFails.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/maxFails/validator* $DOCKER_CONTAINER:/opt/verapdf-rest/config
11 | }
12 | 
13 | teardown() {
14 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
15 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
16 | 
17 |     echo -e "\nDone ..." >&3
18 | }
19 | 
20 | @test "--maxFails, Sets maximum amount of failed checks, maxFails=1" {
21 | 
22 |     run curl -F "file=@$PROJECT_ROOT/Resources/veraPDFPDFAConformanceCheckerGUI.pdf" localhost:8080/api/validate/1b -H "Accept:application/xml"
23 |     assert_output --partial 'failedRules="1"'
24 | 
25 |     [ "$status" -eq 0 ]
26 | }
27 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/maxFails/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/maxNumberOfDisplayedFailedChecks.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 | }
11 | 
12 | teardown() {
13 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
14 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
15 | 
16 |     echo -e "\nDone ..." >&3
17 | }
18 | 
19 | @test "--maxNumberOfDisplayedFailedChecks, Sets maximum amount of failed checks displayed for each rule, maxNumberOfDisplayedFailedChecks=0" {
20 | 
21 |     docker cp $BATS_TEST_DIRNAME/maxNumberOfDisplayedFailedChecks/validator_Checks_0.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
22 | 
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/a.pdf" localhost:8080/api/validate/1b -H "Accept:text/html"
24 |     refute_output --partial 'Rule'
25 | 
26 |     [ "$status" -eq 0 ]
27 | }
28 | 
29 | @test "--maxNumberOfDisplayedFailedChecks, Sets maximum amount of failed checks displayed for each rule, maxNumberOfDisplayedFailedChecks=2" {
30 | 
31 |     docker cp $BATS_TEST_DIRNAME/maxNumberOfDisplayedFailedChecks/validator_Checks_2.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
32 | 
33 |     failedChecks=$(curl -F "file=@$PROJECT_ROOT/Resources/5-t02-fail-a.pdf" localhost:8080/api/validate/1b -H "Accept:application/xml" | grep -w -o '(8 0 obj PDContentStream)' | grep -w -o PDContentStream | wc -w)
34 | 
35 |     echo -e "$failedChecks\n" >&3
36 | 
37 |     run echo $failedChecks
38 |     assert_equal $failedChecks "2"
39 | 
40 |     [ "$status" -eq 0 ]
41 | }
42 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/maxNumberOfDisplayedFailedChecks/validator_Checks_0.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/maxNumberOfDisplayedFailedChecks/validator_Checks_2.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/recordPasses.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/recordPasses/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
11 | 
12 | }
13 | 
14 | teardown() {
15 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
16 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
17 | 
18 |     echo -e "\nDone ..." >&3
19 | }
20 | 
21 | @test "--recordPasses, Logs successful validation checks, recordPasses=true" {
22 | 
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/veraPDFPDFAConformanceCheckerGUI.pdf" localhost:8080/api/validate/1b -H "Accept:text/html"
24 |     assert_output --partial 'Passed'
25 | 
26 |     [ "$status" -eq 0 ]
27 | }
28 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/recordPasses/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/showErrorMessages.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | 
 3 | setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 6 |     _common_setup
 7 | 
 8 |     prepare_fresh_verapdf_config_files
 9 | 
10 |     docker cp $BATS_TEST_DIRNAME/showErrorMessages/validator.xml $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml
11 | 
12 | }
13 | 
14 | teardown() {
15 |     docker cp $DOCKER_CONTAINER:/opt/verapdf-rest/config/validator.xml $BATS_TEST_TMPDIR
16 |     cat $BATS_TEST_TMPDIR/validator.xml >&3
17 | 
18 |     echo -e "\nDone ..." >&3
19 | }
20 | 
21 | @test "--showErrorMessages, Add detailed error message for each check, showErrorMessages=true" {
22 | 
23 |     run curl -F "file=@$PROJECT_ROOT/Resources/a.pdf" localhost:8080/api/validate/1b -H "Accept:text/html"
24 |     assert_output --partial "the PDF/A Identification Schema is null"
25 | 
26 |     [ "$status" -eq 0 ]
27 | }
28 | 


--------------------------------------------------------------------------------
/tests/Options/config/validator/showErrorMessages/validator.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 


--------------------------------------------------------------------------------
/tests/Options/services/bindMountLocalFiles/steps_in_docker.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env sh
 2 | 
 3 | ls -la /home/
 4 | apk update
 5 | apk add --no-cache curl
 6 | cd /home/Res_tmp/
 7 | echo -e "\n"
 8 | 
 9 | curl -F "file=@/home/Res_tmp/a.pdf" localhost:8080/api/validate/3a > res_tmp_log.log -H 'accept: application/xml'
10 | exit
11 | 


--------------------------------------------------------------------------------
/tests/Options/services/bindMountLocalFiles/withBindMountLocalFiles.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | BATS_TEST_TIMEOUT=120 # sec
 3 | 
 4 | setup() {
 5 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 6 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 7 |     _common_setup
 8 | 
 9 |     prepare_fresh_verapdf_config_files
10 | 
11 | }
12 | 
13 | teardown() {
14 |     echo -e "\nDone ..." >&3
15 | }
16 | 
17 | @test "-v, Mounted local files in the Docker container check, bind /tmp:/home/Res_tmp" {
18 | 
19 |     docker cp  $BATS_TEST_DIRNAME/steps_in_docker.sh $DOCKER_CONTAINER:/home/steps_in_docker.sh
20 |     cp -f $PROJECT_ROOT/Resources/a.pdf /tmp/a.pdf
21 | 
22 |     run docker exec -i --user root $DOCKER_CONTAINER  /home/steps_in_docker.sh
23 | 
24 |     echo "$output" >&3
25 | 
26 |     run cat /tmp/res_tmp_log.log
27 | 
28 |     echo "$output" >&3
29 | 
30 |     assert_output --partial ""
31 |     assert_output --partial "a.pdf"
32 |     assert_output --partial 'profileName="PDF/A-3A validation profile"'
33 | 
34 | }
35 | 


--------------------------------------------------------------------------------
/tests/Options/services/limitingFileSize/limitingFileSize.bats:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bats
 2 | BATS_TEST_TIMEOUT=120  # sec
 3 | 
 4 | setup() {
 5 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 6 |     load "$PROJECT_ROOT/tools/test_helper/common-setup.bash"
 7 |     _common_setup
 8 | 
 9 |     prepare_fresh_verapdf_config_files
10 | 
11 | }
12 | 
13 | teardown() {
14 |     echo -e "\nDone ..." >&3
15 | }
16 | 
17 | @test "-e VERAPDF_MAX_FILE_SIZE, Limiting PDF file size, VERAPDF_MAX_FILE_SIZE=1 MB" {
18 | 
19 |     run curl -F "file=@$PROJECT_ROOT/Resources/PDFBOX_allowed_file_size.pdf" localhost:8080/api/validate/3a 
20 | 
21 |     echo "$output" >&3
22 |     assert_output --partial '"code":400'
23 |     assert_output --partial '"Maximum allowed file size exceeded: 1 MB"}'
24 |     
25 |     [ "$status" -eq 0 ]
26 | }
27 | 


--------------------------------------------------------------------------------
/tests/Options/setup_suite.bash:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | setup_suite() {
 4 |   echo -e "Starting tests ... \n" >&3
 5 |   export DOCKER_ID=$(docker ps | grep "rest:" | awk '{print $1}')
 6 | }
 7 | 
 8 | teardown_suite() {
 9 |   echo -e "\nEnding tests ... \n" >&3
10 | 
11 |   # docker logs $DOCKER_ID >&3
12 |   
13 |   sleep 3 # sleep sec
14 | 
15 |   docker logs "$DOCKER_ID" > ./results/container.log 2>&1
16 |   sleep 3 # sleep sec
17 | }
18 | 


--------------------------------------------------------------------------------
/tests/Resources/5-t02-fail-a.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/5-t02-fail-a.pdf


--------------------------------------------------------------------------------
/tests/Resources/6.1.3-01-fail-5.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/6.1.3-01-fail-5.pdf


--------------------------------------------------------------------------------
/tests/Resources/Mustang_505.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/Mustang_505.pdf


--------------------------------------------------------------------------------
/tests/Resources/PDFBOX_allowed_file_size.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/PDFBOX_allowed_file_size.pdf


--------------------------------------------------------------------------------
/tests/Resources/a.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/a.pdf


--------------------------------------------------------------------------------
/tests/Resources/a_for_debug.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/a_for_debug.pdf


--------------------------------------------------------------------------------
/tests/Resources/orange.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/orange.pdf


--------------------------------------------------------------------------------
/tests/Resources/veraPDFPDFAConformanceCheckerGUI.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/veraPDFPDFAConformanceCheckerGUI.pdf


--------------------------------------------------------------------------------
/tests/Resources/veraPDF_MF3.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/Resources/veraPDF_MF3.pdf


--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/__init__.py


--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
 1 | import pytest
 2 | 
 3 | 
 4 | def pytest_addoption(parser):
 5 |     parser.addoption(
 6 |         "--base_url", action="store", default="https://dev.verapdf-rest.duallab.com/"
 7 |     )
 8 | 
 9 | 
10 | @pytest.fixture(scope="session")
11 | def get_base_url(request):
12 |     url = request.config.getoption(
13 |         "--base_url", default="https://dev.verapdf-rest.duallab.com/"
14 |     )
15 |     print("\n\n#### Base_url ...", url)
16 |     return url
17 | 


--------------------------------------------------------------------------------
/tests/rest_api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/model/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/model/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/model/api_info/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/model/api_info/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/model/api_info/api_endpoint.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | from pydantic_xml import element
 3 | from pydantic_xml import BaseXmlModel
 4 | 
 5 | 
 6 | class ApiEndpoint(BaseModel):
 7 |     id: str
 8 |     version: str
 9 |     buildDate: int
10 | 
11 | 
12 | class ApiEndpointXml(BaseXmlModel, tag="ReleaseDetails"):
13 |     id: str = element(tag="id")
14 |     version: str = element(tag="version")
15 |     buildDate: int = element(tag="buildDate")
16 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_info/environment.py:
--------------------------------------------------------------------------------
 1 | from pydantic_xml import BaseXmlModel
 2 | from pydantic import BaseModel
 3 | from tests.rest_api.model.api_info.java import JavaXml
 4 | from tests.rest_api.model.api_info.os import OSXml
 5 | from tests.rest_api.model.api_info.server import ServerXml
 6 | 
 7 | 
 8 | from tests.rest_api.model.api_info.java import Java
 9 | from tests.rest_api.model.api_info.os import OS
10 | from tests.rest_api.model.api_info.server import Server
11 | 
12 | 
13 | class Environment(BaseModel):
14 |     java: Java
15 |     os: OS
16 |     server: Server
17 | 
18 | 
19 | class EnvironmentXml(
20 |     BaseXmlModel,
21 |     tag="Environment",
22 |     # search_mode="unordered",
23 | ):
24 |     os: OSXml
25 |     java: JavaXml
26 |     server: ServerXml
27 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_info/java.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | from pydantic_xml import BaseXmlModel, element
 3 | 
 4 | 
 5 | class Java(BaseModel):
 6 |     vendor: str
 7 |     version: str
 8 |     architecture: str
 9 |     home: str
10 | 
11 | 
12 | class JavaXml(BaseXmlModel, tag="java"):
13 |     vendor: str = element(tag="vendor")
14 |     version: str = element(tag="version")
15 |     architecture: str = element(tag="architecture")
16 |     home: str = element(tag="home")
17 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_info/os.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | from pydantic_xml import BaseXmlModel, element
 3 | 
 4 | 
 5 | class OS(BaseModel):
 6 |     name: str
 7 |     version: str
 8 |     architecture: str
 9 | 
10 | 
11 | class OSXml(BaseXmlModel, tag="os"):
12 |     name: str = element(tag="name")
13 |     version: str = element(tag="version")
14 |     architecture: str = element(tag="architecture")
15 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_info/server.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | from pydantic_xml import BaseXmlModel, element
 3 | 
 4 | 
 5 | class Server(BaseModel):
 6 |     ipAddress: str
 7 |     hostName: str
 8 |     machAddress: str
 9 | 
10 | 
11 | class ServerXml(BaseXmlModel, tag="server"):
12 |     ipAddress: str = element(tag="ipAddress")
13 |     hostName: str = element(tag="hostName")
14 |     machAddress: str = element(tag="machAddress")
15 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/model/api_profiles/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/profile.py:
--------------------------------------------------------------------------------
 1 | from typing import List
 2 | 
 3 | from pydantic import BaseModel
 4 | from pydantic_xml import BaseXmlModel, element
 5 | 
 6 | 
 7 | class Profile(BaseModel):
 8 |     name: str
 9 |     description: str
10 |     creator: str
11 |     dateCreated: int
12 | 
13 | 
14 | class ProfileXMl(BaseXmlModel):
15 |     name: str = element(tag="name")
16 |     description: str = element(tag="description")
17 |     creator: str = element(tag="creator")
18 |     dateCreated: int = element(tag="dateCreated")
19 | 
20 | 
21 | class ProfilesXMl(
22 |     BaseXmlModel,
23 |     tag="HashSet",
24 |     search_mode="unordered",
25 | ):
26 |     items: List[ProfileXMl] = element(tag="item")
27 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/profile_id.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | from pydantic_xml import BaseXmlModel, element
 3 | from tests.rest_api.model.api_profiles.profile import Profile, ProfileXMl
 4 | from tests.rest_api.model.api_profiles.profile_variables import ProfileVariables
 5 | from tests.rest_api.model.api_profiles.profile_rule import ProfileRule
 6 | 
 7 | 
 8 | from typing import List
 9 | 
10 | 
11 | class ProfileID(BaseModel):
12 |     details: Profile
13 |     rules: List[ProfileRule]
14 |     variables: List[ProfileVariables]
15 |     tags: List[str]
16 |     pdfaflavour: str
17 |     hexSha1Digest: str
18 | 
19 | 
20 | class ProfileIDXml(
21 |     BaseXmlModel,
22 |     tag="ValidationProfileImpl",
23 |     search_mode="unordered",
24 | ):
25 |     details: ProfileXMl
26 |     pdfaflavour: str = element(tag="pdfaflavour")
27 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/profile_ids.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 | 
3 | from pydantic_xml import BaseXmlModel, element
4 | 
5 | 
6 | class ProfileIdsXMl(BaseXmlModel, tag="UnmodifiableSet"):
7 |     item: List[str] = element(tag="item")
8 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/profile_rule.py:
--------------------------------------------------------------------------------
 1 | from typing import List
 2 | 
 3 | from pydantic import BaseModel
 4 | from pydantic_xml import BaseXmlModel, element
 5 | 
 6 | from tests.rest_api.model.api_profiles.rule_id import RuleID, RuleIDXml
 7 | 
 8 | 
 9 | class ProfileRule(BaseModel):
10 |     object: str
11 |     description: str
12 |     test: str
13 |     ruleId: RuleID
14 | 
15 | 
16 | class ProfileRuleXml(
17 |     BaseXmlModel,
18 |     search_mode="unordered",
19 | ):
20 |     object: str = element(tag="object")
21 |     description: str = element(tag="description")
22 |     ruleId: RuleIDXml
23 | 
24 | 
25 | class ProfileRuleSXml(
26 |     BaseXmlModel,
27 |     tag="TreeSet",
28 |     search_mode="unordered",
29 | ):
30 |     items: List[ProfileRuleXml] = element(tag="item")
31 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/profile_variables.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 | 
3 | 
4 | class ProfileVariables(BaseModel):
5 |     name: str
6 |     object: str
7 |     defaultValue: str
8 |     value: str
9 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/profiles_flavours.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 | 
3 | from pydantic_xml import BaseXmlModel, element
4 | 
5 | 
6 | class ProfileFlavoursXMl(BaseXmlModel, tag="UnmodifiableSet"):
7 |     item: List[str] = element(tag="item")
8 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_profiles/rule_id.py:
--------------------------------------------------------------------------------
 1 | from typing import List
 2 | 
 3 | from pydantic import BaseModel
 4 | from pydantic_xml import BaseXmlModel, element
 5 | 
 6 | 
 7 | class RuleID(BaseModel):
 8 |     clause: str
 9 |     specification: str
10 |     testNumber: int
11 | 
12 | 
13 | class RuleIDXml(
14 |     BaseXmlModel,
15 | ):
16 |     specification: str = element(tag="specification")
17 |     clause: str = element(tag="clause")
18 |     testNumber: int = element(tag="testNumber")
19 | 
20 | 
21 | class RulesXml(
22 |     BaseXmlModel,
23 |     tag="TreeSet",
24 |     search_mode="unordered",
25 | ):
26 |     items: List[RuleIDXml] = element(tag="item")
27 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_sha1/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/model/api_sha1/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/model/api_sha1/sha1.py:
--------------------------------------------------------------------------------
1 | from pydantic_xml import BaseXmlModel, element
2 | 
3 | 
4 | class Sha1XMl(BaseXmlModel, tag="ByteStreamIdImpl"):
5 |     hexSHA1: str = element(tag="hexSHA1")
6 |     length: int = element(tag="length")
7 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/model/api_validate_details/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/api_validate_details.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | 
 3 | 
 4 | class ApiValidateDetails(BaseModel):
 5 |     id: str
 6 |     name: str
 7 |     version: str
 8 |     provider: str
 9 |     description: str
10 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/job.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 | 
3 | from tests.rest_api.model.api_validate_details.log import Logs
4 | 
5 | 
6 | class Job(BaseModel):
7 |     logs: Logs
8 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/log.py:
--------------------------------------------------------------------------------
 1 | from typing import List
 2 | 
 3 | from pydantic import BaseModel
 4 | 
 5 | 
 6 | class Log(BaseModel):
 7 |     occurrences: int
 8 |     level: str
 9 |     message: str
10 | 
11 | 
12 | class Logs(BaseModel):
13 |     logsCount: int
14 |     logs: List[Log]
15 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/report.py:
--------------------------------------------------------------------------------
 1 | from typing import List
 2 | 
 3 | from pydantic import BaseModel
 4 | 
 5 | from tests.rest_api.model.api_validate_details.summary import BatchSummary
 6 | from tests.rest_api.model.api_validate_details.job import Job
 7 | 
 8 | 
 9 | class Report(BaseModel):
10 |     batchSummary: BatchSummary
11 |     jobs: List[Job]
12 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/result.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 | 
3 | from tests.rest_api.model.api_validate_details.report import Report
4 | 
5 | 
6 | class Result(BaseModel):
7 |     report: Report
8 | 


--------------------------------------------------------------------------------
/tests/rest_api/model/api_validate_details/summary.py:
--------------------------------------------------------------------------------
 1 | from pydantic import BaseModel
 2 | 
 3 | 
 4 | class ValidationSummary(BaseModel):
 5 |     totalJobCount: int
 6 | 
 7 | 
 8 | class BatchSummary(BaseModel):
 9 |     totalJobs: int
10 |     multiJob: bool
11 |     validationSummary: ValidationSummary
12 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/tests/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_info/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/tests/api_info/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_info/test_api.py:
--------------------------------------------------------------------------------
 1 | import re
 2 | import requests
 3 | from tests.rest_api.model.api_info.api_endpoint import ApiEndpoint, ApiEndpointXml
 4 | 
 5 | 
 6 | VERAPDF_REST_ID = "verapdf-rest"
 7 | buildDate_regex = re.compile(r"^[0-9]+$")
 8 | version_regex = re.compile(r"^[0-9]\.[0-9][0-9]\.[0-9]")
 9 | 
10 | 
11 | def test_api_check(get_base_url):
12 |     response = requests.get(get_base_url + "/api")
13 |     assert response.status_code == 200
14 | 
15 |     resp = response.json()
16 |     api = ApiEndpoint(**resp)
17 | 
18 |     assert api.id == VERAPDF_REST_ID
19 |     assert version_regex.match(api.version)
20 |     assert buildDate_regex.match(str(api.buildDate))
21 | 
22 | 
23 | def test_api_xml_check(get_base_url):
24 |     url = get_base_url + "/api"
25 |     headers = {"Accept": "application/xml"}
26 | 
27 |     response = requests.get(url=url, headers=headers)
28 |     api = ApiEndpointXml.from_xml(response.text)
29 | 
30 |     assert api.id == VERAPDF_REST_ID
31 |     assert version_regex.match(api.version)
32 |     assert buildDate_regex.match(str(api.buildDate))
33 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_info/test_api_info.py:
--------------------------------------------------------------------------------
 1 | import re
 2 | import requests
 3 | from tests.rest_api.model.api_info.environment import Environment
 4 | from tests.rest_api.model.api_info.environment import EnvironmentXml
 5 | 
 6 | 
 7 | os_name = re.compile(r".+")
 8 | server_machAddress = re.compile(r"..-..-..-..-..-..")
 9 | 
10 | 
11 | def test_api_info_check(get_base_url):
12 |     response = requests.get(get_base_url + "/api/info")
13 |     assert response.status_code == 200
14 |     resp = response.json()
15 | 
16 |     api_info = Environment(**resp)
17 | 
18 |     assert api_info.java.architecture == "x64"
19 |     assert os_name.match(api_info.os.name)
20 |     assert server_machAddress.match(api_info.server.machAddress)
21 | 
22 | 
23 | def test_api_info_xml_check(get_base_url):
24 |     url = get_base_url + "/api/info"
25 |     headers = {"Accept": "application/xml"}
26 | 
27 |     response = requests.get(url=url, headers=headers)
28 |     assert response.status_code == 200
29 | 
30 |     api_info = EnvironmentXml.from_xml(response.text)
31 | 
32 |     api_info.java.architecture == "x64"
33 |     assert os_name.match(api_info.os.name)
34 |     assert server_machAddress.match(api_info.server.machAddress)
35 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/tests/api_profiles/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/test_api_profiles.py:
--------------------------------------------------------------------------------
 1 | import re
 2 | 
 3 | import requests
 4 | 
 5 | from tests.rest_api.model.api_profiles.profile import Profile, ProfilesXMl
 6 | from tests.rest_api.tests.base_test import BaseClass
 7 | 
 8 | profiles_item_list = []
 9 | profiles_item_xml_list = []
10 | dateCreated_regex = re.compile(r"^[0-9]+$")
11 | description = re.compile(r".+")
12 | 
13 | 
14 | def test_profiles_list_check(get_base_url):
15 |     response = requests.get(get_base_url + "/api/profiles")
16 |     assert response.status_code == 200
17 | 
18 |     profile_list = response.json()
19 | 
20 |     for prof_item in profile_list:
21 |         profile_info = Profile(**prof_item)
22 |         profiles_item_list.append(profile_info.name)
23 |         assert profile_info.creator == BaseClass.CREATOR
24 |         assert description.match(profile_info.description)
25 |         assert dateCreated_regex.match(str(profile_info.dateCreated))
26 |     profiles_item_list.sort()
27 |     assert profiles_item_list == BaseClass.PROFILE_NAMES
28 | 
29 | 
30 | def test_profiles_list_check_xml(get_base_url):
31 |     url = get_base_url + "/api/profiles"
32 |     headers = {"Accept": "application/xml"}
33 | 
34 |     response = requests.get(url, headers=headers)
35 |     assert response.status_code == 200
36 | 
37 |     profiles_list_xml = response.text
38 | 
39 |     profile_list = ProfilesXMl.from_xml(profiles_list_xml)
40 | 
41 |     for item in profile_list.items:
42 |         profiles_item_xml_list.append(item.name)
43 |         assert item.creator == BaseClass.CREATOR
44 |         assert description.match(item.description)
45 |         assert dateCreated_regex.match(str(item.dateCreated))
46 |     profiles_item_xml_list.sort()
47 |     assert profiles_item_xml_list == BaseClass.PROFILE_NAMES
48 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/test_api_profiles_clause.py:
--------------------------------------------------------------------------------
 1 | import pytest
 2 | import requests
 3 | 
 4 | from tests.rest_api.tests.base_test import BaseClass
 5 | from tests.rest_api.model.api_profiles.profile_rule import (
 6 |     ProfileRule,
 7 |     ProfileRuleSXml,
 8 | )
 9 | 
10 | 
11 | @pytest.mark.parametrize(
12 |     "profile_id, expected_clause, expected_clause_items",
13 |     BaseClass.PROFILE_CLAUSE,
14 | )
15 | def test_profile_id_clause_check(
16 |     profile_id, expected_clause, expected_clause_items, get_base_url
17 | ):
18 |     response = requests.get(
19 |         get_base_url + "/api/profiles/" + profile_id + "/" + expected_clause
20 |     )
21 |     assert response.status_code == 200
22 | 
23 |     clause_list = response.json()
24 |     item_list = []
25 | 
26 |     for item in clause_list:
27 |         item_list.append(ProfileRule(**item))
28 | 
29 |     assert len(item_list) == expected_clause_items
30 | 
31 |     for item in item_list:
32 |         assert item.ruleId.clause == expected_clause
33 | 
34 | 
35 | @pytest.mark.parametrize(
36 |     "profile_id, expected_clause, expected_clause_items",
37 |     BaseClass.PROFILE_CLAUSE,
38 | )
39 | def test_profile_id_clause_xml_check(
40 |     profile_id, expected_clause, expected_clause_items, get_base_url
41 | ):
42 |     url = get_base_url + "/api/profiles/" + profile_id + "/" + expected_clause
43 |     headers = {"Accept": "application/xml"}
44 | 
45 |     response = requests.get(url=url, headers=headers)
46 |     assert response.status_code == 200
47 | 
48 |     clause_list_xml = response.text
49 |     clause_list = ProfileRuleSXml.from_xml(clause_list_xml)
50 | 
51 |     assert len(clause_list.items) == expected_clause_items
52 | 
53 |     for item in clause_list.items:
54 |         assert item.ruleId.clause == expected_clause
55 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/test_api_profiles_flavours.py:
--------------------------------------------------------------------------------
 1 | import requests
 2 | 
 3 | from tests.rest_api.model.api_profiles.profiles_flavours import ProfileFlavoursXMl
 4 | from tests.rest_api.tests.base_test import BaseClass
 5 | 
 6 | 
 7 | def test_profile_flavours_check(get_base_url):
 8 |     response = requests.get(get_base_url + "/api/profiles/flavours")
 9 |     assert response.status_code == 200
10 | 
11 |     flavours_list = response.json()
12 |     assert sorted(flavours_list) == BaseClass.FLAVOURS_LIST
13 | 
14 | 
15 | def test_profile_flavours_xml_check(get_base_url):
16 |     url = get_base_url + "/api/profiles/flavours"
17 |     headers = {"Accept": "application/xml"}
18 | 
19 |     response = requests.get(url=url, headers=headers)
20 |     assert response.status_code == 200
21 | 
22 |     flavours_xml = response.text
23 |     flavours_list = ProfileFlavoursXMl.from_xml(flavours_xml)
24 |     assert sorted(flavours_list.item) == BaseClass.FLAVOURS_LIST
25 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/test_api_profiles_id.py:
--------------------------------------------------------------------------------
 1 | import pytest
 2 | import requests
 3 | 
 4 | from tests.rest_api.model.api_profiles.profile_id import ProfileID, ProfileIDXml
 5 | from tests.rest_api.tests.base_test import BaseClass
 6 | 
 7 | 
 8 | @pytest.mark.parametrize(
 9 |     "profile_id, expected_pdfaflavour,  expected_name",
10 |     BaseClass.PROFILE_LIST,
11 | )
12 | def test_profile_id_check(
13 |     profile_id, expected_pdfaflavour, expected_name, get_base_url
14 | ):
15 |     response = requests.get(get_base_url + "/api/profiles/" + profile_id)
16 |     assert response.status_code == 200
17 | 
18 |     profile_id = response.json()
19 |     profile_id_info = ProfileID(**profile_id)
20 | 
21 |     assert profile_id_info.details.name == expected_name
22 |     assert profile_id_info.details.creator == BaseClass.CREATOR
23 |     assert profile_id_info.pdfaflavour == expected_pdfaflavour
24 | 
25 | 
26 | @pytest.mark.parametrize(
27 |     "profile_id, expected_pdfaflavour,  expected_name",
28 |     BaseClass.PROFILE_LIST,
29 | )
30 | def test_profile_id_xml_check(
31 |     profile_id, expected_pdfaflavour, expected_name, get_base_url
32 | ):
33 |     url = get_base_url + "/api/profiles/" + profile_id
34 |     headers = {"Accept": "application/xml"}
35 | 
36 |     response = requests.get(url, headers=headers)
37 |     assert response.status_code == 200
38 | 
39 |     profile_id_xml = response.text
40 |     profile_id = ProfileIDXml.from_xml(profile_id_xml)
41 | 
42 |     assert profile_id.pdfaflavour == expected_pdfaflavour
43 |     assert profile_id.details.creator == BaseClass.CREATOR
44 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/test_api_profiles_ids.py:
--------------------------------------------------------------------------------
 1 | import requests
 2 | 
 3 | from tests.rest_api.model.api_profiles.profile_ids import ProfileIdsXMl
 4 | from tests.rest_api.tests.base_test import BaseClass
 5 | 
 6 | 
 7 | def test_profile_ids_check(get_base_url):
 8 |     response = requests.get(get_base_url + "/api/profiles/ids")
 9 |     assert response.status_code == 200
10 | 
11 |     profile_ids = response.json()
12 | 
13 |     assert sorted(profile_ids) == BaseClass.PROFILE_IDS
14 | 
15 | 
16 | def test_profile_ids_xml_check(get_base_url):
17 |     url = get_base_url + "/api/profiles/ids"
18 |     headers = {"Accept": "application/xml"}
19 | 
20 |     response = requests.get(url, headers=headers)
21 |     assert response.status_code == 200
22 | 
23 |     profile_ids_xml = response.text
24 |     profile_ids_list = ProfileIdsXMl.from_xml(profile_ids_xml)
25 | 
26 |     assert sorted(profile_ids_list.item) == BaseClass.PROFILE_IDS
27 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_profiles/test_api_profiles_ruleids.py:
--------------------------------------------------------------------------------
 1 | import pytest
 2 | import requests
 3 | 
 4 | from tests.rest_api.model.api_profiles.rule_id import RuleID, RulesXml
 5 | from tests.rest_api.tests.base_test import BaseClass
 6 | 
 7 | 
 8 | @pytest.mark.parametrize(
 9 |     "profile_id, expected_rules_count",
10 |     BaseClass.PROFILE_RULES,
11 | )
12 | def test_ruleids_check(profile_id, expected_rules_count, get_base_url):
13 |     response = requests.get(get_base_url + "/api/profiles/" + profile_id + "/ruleids")
14 |     assert response.status_code == 200
15 |     item_list = []
16 | 
17 |     rules = response.json()
18 | 
19 |     for item in rules:
20 |         item_list.append(RuleID(**item))
21 | 
22 |     assert len(item_list) == expected_rules_count
23 | 
24 | 
25 | @pytest.mark.parametrize(
26 |     "profile_id, expected_rules_count",
27 |     BaseClass.PROFILE_RULES,
28 | )
29 | def test_ruleids_xml_check(profile_id, expected_rules_count, get_base_url):
30 |     url = get_base_url + "/api/profiles/" + profile_id + "/ruleids"
31 |     headers = {"Accept": "application/xml"}
32 | 
33 |     response = requests.get(url, headers=headers)
34 |     assert response.status_code == 200
35 | 
36 |     rules_xml = response.text
37 | 
38 |     rules_info = RulesXml.from_xml(rules_xml)
39 |     assert len(rules_info.items) == expected_rules_count
40 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_sha1/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/tests/api_sha1/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_sha1/api_sha1.py:
--------------------------------------------------------------------------------
 1 | import requests
 2 | 
 3 | from tests.rest_api.model.api_sha1.sha1 import Sha1XMl
 4 | 
 5 | 
 6 | def test_sha1_check(get_base_url):
 7 |     headers = {
 8 |         "accept": "application/json",
 9 |         # requests won't add a boundary if this header is set when you pass files=
10 |         # 'Content-Type': 'multipart/form-data',
11 |     }
12 |     files = {
13 |         "file": (
14 |             "a.pdf",
15 |             open("..//../../../tests/Resources/a.pdf", "rb"),
16 |             "application/pdf",
17 |         ),
18 |     }
19 | 
20 |     response = requests.post(
21 |         "https://demo.verapdf.org/api/sha1", headers=headers, files=files
22 |     )
23 | 
24 |     sha1_info = response.json()
25 | 
26 |     assert sha1_info["hexSHA1"] == "bb0a429d5449548a50de3d5bd2636d4f918272c4"
27 |     assert sha1_info["length"] == 3175
28 | 
29 | 
30 | def test_sha1_xml_check(get_base_url):
31 |     headers = {
32 |         "accept": "application/xml",
33 |         # requests won't add a boundary if this header is set when you pass files=
34 |         # 'Content-Type': 'multipart/form-data',
35 |     }
36 |     files = {
37 |         "file": (
38 |             "a.pdf",
39 |             open("..//../../../tests/Resources/a.pdf", "rb"),
40 |             "application/pdf",
41 |         ),
42 |     }
43 | 
44 |     response = requests.post(
45 |         "https://demo.verapdf.org/api/sha1", headers=headers, files=files
46 |     )
47 | 
48 |     sha1_info_xml = response.text
49 | 
50 |     sha1_info = Sha1XMl.from_xml(sha1_info_xml)
51 | 
52 |     assert sha1_info.hexSHA1 == "bb0a429d5449548a50de3d5bd2636d4f918272c4"
53 |     assert sha1_info.length == 3175
54 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_sha1/test_api_sha1_null.py:
--------------------------------------------------------------------------------
 1 | import requests
 2 | 
 3 | from tests.rest_api.model.api_sha1.sha1 import Sha1XMl
 4 | 
 5 | 
 6 | def test_sha1_null_check(get_base_url):
 7 |     response = requests.get(get_base_url + "/api/sha1/null")
 8 |     assert response.status_code == 200
 9 | 
10 |     profile_ids = response.json()
11 | 
12 |     assert profile_ids["hexSHA1"] == "da39a3ee5e6b4b0d3255bfef95601890afd80709"
13 |     assert profile_ids["length"] == 0
14 | 
15 | 
16 | def test_sha1_null_xml_check(get_base_url):
17 |     url = get_base_url + "/api/sha1/null"
18 |     headers = {"Accept": "application/xml"}
19 | 
20 |     response = requests.get(url, headers=headers)
21 |     assert response.status_code == 200
22 | 
23 |     sha1_null_xml = response.text
24 | 
25 |     sha1_null = Sha1XMl.from_xml(sha1_null_xml)
26 | 
27 |     assert sha1_null.hexSHA1 == "da39a3ee5e6b4b0d3255bfef95601890afd80709"
28 |     assert sha1_null.length == 0
29 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_validate/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veraPDF/veraPDF-rest/b514aa0585fee0e2a82f3e086f79f71b61677f96/tests/rest_api/tests/api_validate/__init__.py


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_validate/test_api_validate_details.py:
--------------------------------------------------------------------------------
 1 | import re
 2 | 
 3 | import requests
 4 | 
 5 | from tests.rest_api.model.api_validate_details.api_validate_details import (
 6 |     ApiValidateDetails,
 7 | )
 8 | 
 9 | details_version = re.compile(r"\d.\d\d.\d+")
10 | 
11 | 
12 | def test_api_validate_details_check(get_base_url):
13 |     response = requests.get(get_base_url + "/api/validate/details")
14 |     assert response.status_code == 200
15 | 
16 |     details = ApiValidateDetails(**(response.json()))
17 | 
18 |     assert details.id == "http://pdfa.verapdf.org/foundry#verapdf"
19 |     assert details.name == "VeraPDF Foundry"
20 |     assert details_version.match(details.version)
21 |     assert details.provider == "The veraPDF Consortium."
22 |     assert details.description == "veraPDF greenfield foundry instance."
23 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_validate/test_api_validate_profileId.py:
--------------------------------------------------------------------------------
 1 | from io import StringIO
 2 | 
 3 | import pytest
 4 | import requests
 5 | import xmltodict
 6 | from lxml import etree
 7 | 
 8 | from tests.rest_api.tests.base_test import BaseClass
 9 | 
10 | 
11 | @pytest.mark.parametrize(
12 |     "profile_id, expected_profile_name",
13 |     BaseClass.VALIDATION_LIST,
14 | )
15 | def test_api_validate_details_check(profile_id, expected_profile_name, get_base_url):
16 |     url = get_base_url + "/api/validate/" + profile_id
17 |     headers = {
18 |         "accept": "application/json",
19 |         # requests won't add a boundary if this header is set when you pass files=
20 |         # 'Content-Type': 'multipart/form-data',
21 |     }
22 | 
23 |     files = {
24 |         "file": ("a.pdf", open(BaseClass.FILE_TO_PARSE, "rb"), "application/pdf"),
25 |     }
26 | 
27 |     response = requests.post(url, headers=headers, files=files)
28 | 
29 |     details = response.json()
30 | 
31 |     assert "a.pdf" in (details["report"]["jobs"][0]["itemDetails"]["name"])
32 |     assert (
33 |         details["report"]["jobs"][0]["validationResult"][0]["profileName"]
34 |         == expected_profile_name
35 |     )
36 | 
37 | 
38 | @pytest.mark.parametrize(
39 |     "profile_id, expected_profile_name",
40 |     BaseClass.VALIDATION_LIST,
41 | )
42 | def test_api_validate_details_xml_check(
43 |     profile_id, expected_profile_name, get_base_url
44 | ):
45 |     url = get_base_url + "/api/validate/" + profile_id
46 |     headers = {
47 |         "accept": "application/xml",
48 |         # requests won't add a boundary if this header is set when you pass files=
49 |         # 'Content-Type': 'multipart/form-data',
50 |     }
51 | 
52 |     files = {
53 |         "file": ("a.pdf", open(BaseClass.FILE_TO_PARSE, "rb"), "application/pdf"),
54 |     }
55 | 
56 |     response = requests.post(url, headers=headers, files=files)
57 | 
58 |     details = xmltodict.parse(response.text)
59 | 
60 |     assert (
61 |         details["report"]["jobs"]["job"]["validationReport"]["@profileName"]
62 |         == expected_profile_name
63 |     )
64 | 
65 | 
66 | @pytest.mark.parametrize(
67 |     "profile_id, expected_profile_name",
68 |     BaseClass.VALIDATION_LIST,
69 | )
70 | def test_api_validate_details_html_check(
71 |     profile_id, expected_profile_name, get_base_url
72 | ):
73 |     url = get_base_url + "/api/validate/" + profile_id
74 |     headers = {
75 |         "accept": "text/html",
76 |         # requests won't add a boundary if this header is set when you pass files=
77 |         # 'Content-Type': 'multipart/form-data',
78 |     }
79 | 
80 |     files = {
81 |         "file": ("a.pdf", open(BaseClass.FILE_TO_PARSE, "rb"), "application/pdf"),
82 |     }
83 |     parser = etree.HTMLParser()
84 | 
85 |     page = requests.post(url, headers=headers, files=files)
86 | 
87 |     html = page.content.decode("utf-8")
88 |     tree = etree.parse(StringIO(html), parser=parser)
89 |     file = tree.xpath('//*[@id="table1"]//tr[1]//td[2]/text()')[0]
90 |     profile = tree.xpath('//*[@id="table1"]//tr[2]//td[2]/a[1]/text()')[0]
91 | 
92 |     assert file == "a.pdf"
93 |     assert profile == expected_profile_name
94 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_validate/test_api_validate_sha_profileId.py:
--------------------------------------------------------------------------------
  1 | from io import StringIO
  2 | 
  3 | import pytest
  4 | import requests
  5 | import xmltodict
  6 | from lxml import etree
  7 | 
  8 | from tests.rest_api.tests.base_test import BaseClass
  9 | 
 10 | 
 11 | @pytest.mark.parametrize(
 12 |     "profile_id, expected_profile_name",
 13 |     BaseClass.VALIDATION_LIST,
 14 | )
 15 | def test_api_validate_sha_profileid_check(
 16 |     profile_id, expected_profile_name, get_base_url
 17 | ):
 18 |     headers = {
 19 |         "accept": "application/json",
 20 |         # requests won't add a boundary if this header is set when you pass files=
 21 |         # 'Content-Type': 'multipart/form-data',
 22 |     }
 23 | 
 24 |     files = {
 25 |         "sha1Hex": (None, "bb0a429d5449548a50de3d5bd2636d4f918272c4"),
 26 |         "file": ("a.pdf", open(BaseClass.FILE_TO_PARSE, "rb"), "application/pdf"),
 27 |     }
 28 | 
 29 |     response = requests.post(
 30 |         get_base_url + "/api/validate/" + profile_id, headers=headers, files=files
 31 |     )
 32 | 
 33 |     assert response.status_code == 200
 34 | 
 35 |     sha1_info = response.json()
 36 | 
 37 |     assert sha1_info["report"]["jobs"][0]["itemDetails"]["name"] == "a.pdf"
 38 |     assert (
 39 |         sha1_info["report"]["jobs"][0]["validationResult"][0]["profileName"]
 40 |         == expected_profile_name
 41 |     )
 42 | 
 43 | 
 44 | @pytest.mark.parametrize(
 45 |     "profile_id, expected_profile_name",
 46 |     BaseClass.VALIDATION_LIST,
 47 | )
 48 | def test_api_validate_sha_profileid_xml_check(
 49 |     profile_id, expected_profile_name, get_base_url
 50 | ):
 51 |     headers = {
 52 |         "accept": "application/xml",
 53 |         # requests won't add a boundary if this header is set when you pass files=
 54 |         # 'Content-Type': 'multipart/form-data',
 55 |     }
 56 | 
 57 |     files = {
 58 |         "sha1Hex": (None, "bb0a429d5449548a50de3d5bd2636d4f918272c4"),
 59 |         "file": ("a.pdf", open(BaseClass.FILE_TO_PARSE, "rb"), "application/pdf"),
 60 |     }
 61 | 
 62 |     response = requests.post(
 63 |         get_base_url + "/api/validate/" + profile_id, headers=headers, files=files
 64 |     )
 65 | 
 66 |     assert response.status_code == 200
 67 | 
 68 |     details = xmltodict.parse(response.text)
 69 | 
 70 |     assert details["report"]["jobs"]["job"]["item"]["name"] == "a.pdf"
 71 |     assert (
 72 |         details["report"]["jobs"]["job"]["validationReport"]["@profileName"]
 73 |         == expected_profile_name
 74 |     )
 75 | 
 76 | 
 77 | @pytest.mark.parametrize(
 78 |     "profile_id, expected_profile_name",
 79 |     BaseClass.VALIDATION_LIST,
 80 | )
 81 | def test_api_validate_sha_profileid_html_check(
 82 |     profile_id, expected_profile_name, get_base_url
 83 | ):
 84 |     url = get_base_url + "/api/validate/" + profile_id
 85 |     headers = {
 86 |         "accept": "text/html",
 87 |         # requests won't add a boundary if this header is set when you pass files=
 88 |         # 'Content-Type': 'multipart/form-data',
 89 |     }
 90 | 
 91 |     files = {
 92 |         "sha1Hex": (None, "bb0a429d5449548a50de3d5bd2636d4f918272c4"),
 93 |         "file": ("a.pdf", open(BaseClass.FILE_TO_PARSE, "rb"), "application/pdf"),
 94 |     }
 95 | 
 96 |     parser = etree.HTMLParser()
 97 | 
 98 |     page = requests.post(url, headers=headers, files=files)
 99 | 
100 |     assert page.status_code == 200
101 | 
102 |     html = page.content.decode("utf-8")
103 |     tree = etree.parse(StringIO(html), parser=parser)
104 | 
105 |     file = tree.xpath('//*[@id="table1"]//tr[1]//td[2]/text()')[0]
106 |     profile = tree.xpath('//*[@id="table1"]//tr[2]//td[2]/a[1]/text()')[0]
107 | 
108 |     assert file == "a.pdf"
109 |     assert profile == expected_profile_name
110 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/api_validate/test_validate_pdf_by_url.py:
--------------------------------------------------------------------------------
 1 | import pytest
 2 | import requests
 3 | 
 4 | from tests.rest_api.model.api_validate_details.result import Report, Result
 5 | from tests.rest_api.tests.base_test import BaseClass
 6 | 
 7 | 
 8 | @pytest.mark.parametrize(
 9 |     "file_url, expected_code, json_key, expected_message",
10 |     [
11 |         ("", 400, "message", "URL is empty"),
12 |         (
13 |             "https://abc.qwerty.com",
14 |             400,
15 |             "message",
16 |             "URL is incorrect: https://abc.qwerty.com",
17 |         ),
18 |     ],
19 | )
20 | @pytest.mark.parametrize(
21 |     "profile_id",
22 |     BaseClass.PROFILE_IDS,
23 | )
24 | def test_validate_pdf_with_fail_url(
25 |     file_url, expected_code, json_key, expected_message, profile_id, get_base_url
26 | ):
27 |     url = get_base_url + "/api/validate/url/" + profile_id
28 | 
29 |     headers = {
30 |         "accept": "application/json",
31 |         # requests won't add a boundary if this header is set when you pass files=
32 |         # 'Content-Type': 'multipart/form-data',
33 |     }
34 | 
35 |     files = {
36 |         "url": (None, file_url),
37 |     }
38 | 
39 |     response = requests.post(url=url, headers=headers, files=files)
40 | 
41 |     assert response.status_code == expected_code
42 |     assert expected_message == response.json()[json_key]
43 | 
44 | 
45 | @pytest.mark.parametrize(
46 |     "file_url, expected_code",
47 |     [
48 |         (
49 |             "https://github.com/veraPDF/veraPDF-regression-tests/blob/integration/CLI/Resources/a.pdf",
50 |             200,
51 |         ),
52 |     ],
53 | )
54 | @pytest.mark.parametrize(
55 |     "profile_id",
56 |     BaseClass.PROFILE_IDS,
57 | )
58 | def test_validate_pdf_with_pass_url(file_url, expected_code, profile_id, get_base_url):
59 |     url = get_base_url + "/api/validate/url/" + profile_id
60 | 
61 |     headers = {
62 |         "accept": "application/json",
63 |         # requests won't add a boundary if this header is set when you pass files=
64 |         # 'Content-Type': 'multipart/form-data',
65 |     }
66 | 
67 |     files = {
68 |         "url": (None, file_url),
69 |     }
70 | 
71 |     response = requests.post(url=url, headers=headers, files=files)
72 | 
73 |     result = Result(**response.json())
74 |     assert result.report.batchSummary.totalJobs == 1
75 |     assert result.report.jobs[0].logs.logs[0].level == "WARNING"
76 |     assert result.report.jobs[0].logs.logs[0].occurrences == 1
77 |     assert "a.pdf" in result.report.jobs[0].logs.logs[0].message
78 | 


--------------------------------------------------------------------------------
/tests/rest_api/tests/base_test.py:
--------------------------------------------------------------------------------
 1 | """ Possible urls:
 2 | https://demo.verapdf.org/swagger
 3 | https://dev.verapdf-rest.duallab.com/swagger
 4 | http://localhost:8080/api
 5 | """
 6 | import itertools
 7 | 
 8 | 
 9 | class BaseClass:
10 |     CREATOR = "veraPDF Consortium"
11 |     FILE_TO_PARSE = "./tests/Resources/a.pdf"
12 |     PROFILE_GENERAL = (
13 |         # 1b, 1a, 2b, 2a, 2u, 3b, 3a, 3u, 4, 4e, 4f, ua1, ua2, wt1a or wt1r
14 |         # profile id, pdfa flavour, profile name, profile rule count, clause & clause items
15 |         ("1b", "PDFA_1_B", "PDF/A-1B validation profile", 129, ("6.7.3", 8)),
16 |         ("1a", "PDFA_1_A", "PDF/A-1A validation profile", 135, ("6.1.7", 3)),
17 |         ("2b", "PDFA_2_B", "PDF/A-2B validation profile", 144, ("6.2.11.3.1", 1)),
18 |         ("2a", "PDFA_2_A", "PDF/A-2A validation profile", 153, ("6.7.3.4", 3)),
19 |         ("2u", "PDFA_2_U", "PDF/A-2U validation profile", 146, ("6.2.11.3.1", 1)),
20 |         ("3a", "PDFA_3_A", "PDF/A-3A validation profile", 155, ("6.2.11.3.2", 1)),
21 |         ("3b", "PDFA_3_B", "PDF/A-3B validation profile", 146, ("6.2.11.3.2", 1)),
22 |         ("3u", "PDFA_3_U", "PDF/A-3U validation profile", 148, ("6.3.1", 1)),
23 |         ("4", "PDFA_4", "PDF/A-4 validation profile", 109, ("6.6.1", 2)),
24 |         ("4e", "PDFA_4_E", "PDF/A-4E validation profile", 109, ("6.1.6.2", 1)),
25 |         ("4f", "PDFA_4_F", "PDF/A-4F validation profile", 109, ("6.2.4.2", 3)),
26 |         ("ua1", "PDFUA_1", "PDF/UA-1 validation profile", 106, ("7.18.8", 1)),
27 |         ("ua2", "PDFUA_2", "PDF/UA-2 + Tagged PDF validation profile", 1723, ("5", 5)),
28 |         ("wt1a", "WTPDF_1_0_ACCESSIBILITY", "WTPDF 1.0 Accessibility validation profile", 1719, ("8.4.3", 3)),
29 |         ("wt1r", "WTPDF_1_0_REUSE", "WTPDF 1.0 Reuse validation profile", 1706, ("8.4.5.4", 3)),
30 |     )
31 | 
32 |     FLAVOURS_LIST = sorted(
33 |         [
34 |             item[0]
35 |             for item in [
36 |                 list(itertools.compress(item, [0, 1, 0, 0, 0]))
37 |                 for item in PROFILE_GENERAL
38 |             ]
39 |         ]
40 |     )
41 |     VALIDATION_LIST = [
42 |         list(itertools.compress(item, [1, 0, 1, 0, 0])) for item in PROFILE_GENERAL
43 |     ]
44 | 
45 |     PROFILE_LIST = [
46 |         list(itertools.compress(item, [1, 1, 1, 0, 0])) for item in PROFILE_GENERAL
47 |     ]
48 |     PROFILE_IDS = sorted(
49 |         [
50 |             item[0]
51 |             for item in [
52 |                 list(itertools.compress(item, [1, 0, 0, 0, 0]))
53 |                 for item in PROFILE_GENERAL
54 |             ]
55 |         ]
56 |     )
57 |     PROFILE_NAMES = sorted(
58 |         [
59 |             item[0]
60 |             for item in [
61 |                 list(itertools.compress(item, [0, 0, 1, 0, 0]))
62 |                 for item in PROFILE_GENERAL
63 |             ]
64 |         ]
65 |     )
66 |     PROFILE_RULES = [
67 |         list(itertools.compress(item, [1, 0, 0, 1, 0])) for item in PROFILE_GENERAL
68 |     ]
69 | 
70 |     PROFILE_CLAUSE = []
71 |     for i in list(
72 |         ([list(itertools.compress(item, [1, 0, 0, 0, 1])) for item in PROFILE_GENERAL])
73 |     ):
74 |         x, (y, z) = i
75 |         PROFILE_CLAUSE.append([x, y, z])
76 | 


--------------------------------------------------------------------------------
/tests/tools/test_helper/common-setup.bash:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | _common_setup() {
 4 |     PROJECT_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../.." >/dev/null 2>&1 && pwd)"
 5 |     PATH="$PROJECT_ROOT/tools/test_helper/:$PATH"
 6 | 
 7 |     load "$PROJECT_ROOT/bats-support/load.bash"
 8 |     load "$PROJECT_ROOT/bats-assert/load.bash"
 9 | 
10 |     export DOCKER_CONTAINER=$(echo $(docker ps | grep "rest:" | awk '{print $1}'))
11 |     echo -e "\nDOCKER_CONTAINER_ID: $DOCKER_CONTAINER \n" >&3
12 | 
13 |     echo "Checking docker container logs...at:" >&3
14 |     sleep 1 # sleep sec
15 | 
16 |     export test_logs_starting_time=$(date +%s)
17 |     echo $(date -d @"$test_logs_starting_time") >&3
18 | }
19 | 
20 | remove_verapdf_config_files() {
21 |     echo -e "Removing files ... " >&3
22 |     echo -e $(docker exec -i --user=root "$DOCKER_CONTAINER" ls -la /opt/verapdf-rest/config/) >&3
23 | 
24 |     docker exec --user=root "$DOCKER_CONTAINER" rm -rf /opt/verapdf-rest/config/features.xml
25 |     docker exec --user=root "$DOCKER_CONTAINER" rm -rf /opt/verapdf-rest/config/fixer.xml
26 |     docker exec --user=root "$DOCKER_CONTAINER" rm -rf /opt/verapdf-rest/config/plugins.xml
27 |     docker exec --user=root "$DOCKER_CONTAINER" rm -rf /opt/verapdf-rest/config/validator.xml
28 |     docker exec --user=root "$DOCKER_CONTAINER" rm -rf /opt/verapdf-rest/config/app.xml
29 |     
30 |     echo -e "Removing files ... Done\n" >&3
31 | 
32 |     echo -e "Files in the container ... " >&3
33 |     echo -e $(docker exec -i --user=root "$DOCKER_CONTAINER" ls -la /opt/verapdf-rest/config/) >&3
34 |     echo -e "\n" >&3
35 | }
36 | 
37 | prepare_fresh_verapdf_config_files() {
38 | 
39 |     remove_verapdf_config_files
40 | 
41 |     echo -e "Coping new files ... to the container" >&3
42 |     echo "file list" $(ls "$PROJECT_ROOT"/../config) >&3
43 | 
44 |     docker cp  "$PROJECT_ROOT"/../config/features.xml "$DOCKER_CONTAINER":/opt/verapdf-rest/config/features.xml
45 |     docker cp  "$PROJECT_ROOT"/../config/fixer.xml "$DOCKER_CONTAINER":/opt/verapdf-rest/config/fixer.xml
46 |     docker cp  "$PROJECT_ROOT"/../config/plugins.xml "$DOCKER_CONTAINER":/opt/verapdf-rest/config/plugins.xml
47 |     docker cp  "$PROJECT_ROOT"/../config/validator.xml "$DOCKER_CONTAINER":/opt/verapdf-rest/config/validator.xml
48 |     docker cp  "$PROJECT_ROOT"/../config/app.xml "$DOCKER_CONTAINER":/opt/verapdf-rest/config/app.xml
49 | 
50 |     echo -e "Files in the container ... " >&3
51 |     echo -e $(docker exec -i --user=root "$DOCKER_CONTAINER" ls -la /opt/verapdf-rest/config/) >&3
52 |     echo -e "\n" >&3
53 | }
54 | 


--------------------------------------------------------------------------------