├── .github ├── dependabot.yml └── workflows │ ├── codeql.yml │ ├── dependency-review.yml │ ├── deploy-docker.yml │ ├── maven-pulls.yml │ ├── maven.yml │ ├── prepare-release.yml │ └── release.yml ├── .gitignore ├── .whitesource ├── CI ├── CI.md ├── docker-release.sh ├── ghApiClient.py ├── lastRelease.py ├── post-release.sh ├── pre-release.sh ├── prepare-release.sh ├── publishRelease.py ├── releaseNotes.py └── version.sh ├── Dockerfile ├── LICENSE ├── NOTICE ├── README.md ├── inflector.yaml ├── pom.xml └── src └── main ├── java └── io │ └── swagger │ └── handler │ └── ConverterController.java ├── swagger └── swagger.yaml └── webapp ├── WEB-INF └── web.xml └── index.html /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | target-branch: "master" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | ignore: 9 | - dependency-name: "*" 10 | update-types: ["version-update:semver-major"] -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | schedule: 16 | - cron: '16 04 * * 2' 17 | 18 | jobs: 19 | analyze: 20 | name: Analyze (${{ matrix.language }}) 21 | # Runner size impacts CodeQL analysis time. To learn more, please see: 22 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 23 | # - https://gh.io/supported-runners-and-hardware-resources 24 | # - https://gh.io/using-larger-runners (GitHub.com only) 25 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 26 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 27 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 28 | permissions: 29 | # required for all workflows 30 | security-events: write 31 | 32 | # required to fetch internal or private CodeQL packs 33 | packages: read 34 | 35 | # only required for workflows in private repositories 36 | actions: read 37 | contents: read 38 | 39 | strategy: 40 | fail-fast: false 41 | matrix: 42 | include: 43 | - language: java-kotlin 44 | build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too. 45 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 46 | # Use `c-cpp` to analyze code written in C, C++ or both 47 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 48 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 49 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 50 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 51 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 52 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 53 | steps: 54 | - name: Checkout repository 55 | uses: actions/checkout@v4 56 | 57 | # Initializes the CodeQL tools for scanning. 58 | - name: Initialize CodeQL 59 | uses: github/codeql-action/init@v3 60 | with: 61 | languages: ${{ matrix.language }} 62 | build-mode: ${{ matrix.build-mode }} 63 | # If you wish to specify custom queries, you can do so here or in a config file. 64 | # By default, queries listed here will override any specified in a config file. 65 | # Prefix the list here with "+" to use these queries and those in the config file. 66 | 67 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 68 | # queries: security-extended,security-and-quality 69 | 70 | # If the analyze step fails for one of the languages you are analyzing with 71 | # "We were unable to automatically build your code", modify the matrix above 72 | # to set the build mode to "manual" for that language. Then modify this step 73 | # to build your code. 74 | # ℹ️ Command-line programs to run using the OS shell. 75 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 76 | - if: matrix.build-mode == 'manual' 77 | shell: bash 78 | run: | 79 | echo 'If you are using a "manual" build mode for one or more of the' \ 80 | 'languages you are analyzing, replace this with the commands to build' \ 81 | 'your code, for example:' 82 | echo ' make bootstrap' 83 | echo ' make release' 84 | exit 1 85 | 86 | - name: Perform CodeQL Analysis 87 | uses: github/codeql-action/analyze@v3 88 | with: 89 | category: "/language:${{matrix.language}}" -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | name: 'Dependency Review' 2 | on: [pull_request] 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | dependency-review: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 'Checkout Repository' 12 | uses: actions/checkout@v4 13 | - name: Dependency Review 14 | uses: actions/dependency-review-action@v3 15 | with: 16 | fail-on-severity: high 17 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docker.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docker 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: ["master"] 6 | inputs: 7 | tag: 8 | description: tag/version to deploy 9 | required: true 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: deploy docker 17 | run: | 18 | SC_RELEASE_TAG="v${{ env.TAG }}" 19 | echo "$SC_RELEASE_TAG" 20 | 21 | TOKEN="${{ secrets.RANCHER2_BEARER_TOKEN }}" 22 | RANCHER_HOST="rancher.tools.swagger.io" 23 | CLUSTER_ID="c-n8zp2" 24 | NAMESPACE_NAME="swagger-oss" 25 | K8S_OBJECT_TYPE="daemonsets" 26 | K8S_OBJECT_NAME="swagger-converter" 27 | DEPLOY_IMAGE="swaggerapi/swagger-converter:$SC_RELEASE_TAG" 28 | 29 | workloadStatus="" 30 | getStatus() { 31 | echo "Getting update status..." 32 | if ! workloadStatus="$(curl -s -X GET \ 33 | -H "Authorization: Bearer ${TOKEN}" \ 34 | -H 'Content-Type: application/json' \ 35 | "https://${RANCHER_HOST}/k8s/clusters/${CLUSTER_ID}/apis/apps/v1/namespaces/${NAMESPACE_NAME}/${K8S_OBJECT_TYPE}/${K8S_OBJECT_NAME}/status")" 36 | then 37 | echo 'ERROR - get status k8s API call failed!' 38 | echo "Exiting build"... 39 | exit 1 40 | fi 41 | } 42 | 43 | # $1 = image to deploy 44 | updateObject() { 45 | local image="${1}" 46 | echo "Updating image value..." 47 | 48 | if ! curl -s -X PATCH \ 49 | -H "Authorization: Bearer ${TOKEN}" \ 50 | -H 'Content-Type: application/json-patch+json' \ 51 | "https://${RANCHER_HOST}/k8s/clusters/${CLUSTER_ID}/apis/apps/v1/namespaces/${NAMESPACE_NAME}/${K8S_OBJECT_TYPE}/${K8S_OBJECT_NAME}" \ 52 | -d "[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/image\", \"value\": \"${image}\"}]" 53 | then 54 | echo 'ERROR - image update k8s API call failed!' 55 | echo "Exiting build..." 56 | exit 1 57 | fi 58 | } 59 | 60 | 61 | # Check that the TAG is valid 62 | if [[ $SC_RELEASE_TAG =~ ^[vV]?[0-9]*\.[0-9]*\.[0-9]*$ ]]; then 63 | echo "" 64 | echo "This is a Valid TAG..." 65 | 66 | # Get current image/tag in case we need to rollback 67 | getStatus 68 | ROLLBACK_IMAGE="$(echo "${workloadStatus}" | jq -r '.spec.template.spec.containers[0].image')" 69 | echo "" 70 | echo "Current image: ${ROLLBACK_IMAGE}" 71 | 72 | # Update image and validate response 73 | echo "" 74 | updateObject "${DEPLOY_IMAGE}" 75 | echo "" 76 | 77 | echo "" 78 | echo "Waiting for pods to start..." 79 | echo "" 80 | sleep 60s 81 | 82 | # Get state of the k8s object. If numberReady == desiredNumberScheduled, consider the upgrade successful. Else raise error 83 | getStatus 84 | status="$(echo "${workloadStatus}" | jq '.status')" 85 | echo "" 86 | echo "${status}" 87 | echo "" 88 | 89 | numberDesired="$(echo "${status}" | jq -r '.desiredNumberScheduled')" 90 | numberReady="$(echo "${status}" | jq -r '.numberReady')" 91 | 92 | if (( numberReady == numberDesired )); then 93 | echo "${K8S_OBJECT_NAME} has been upgraded to ${DEPLOY_IMAGE}" 94 | 95 | # If pods are not starting, rollback the upgrade and exit the build with error 96 | else 97 | echo "state = error...rolling back upgrade" 98 | updateObject "${ROLLBACK_IMAGE}" 99 | echo "" 100 | 101 | echo "" 102 | echo "Waiting for rollback pods to start..." 103 | echo "" 104 | sleep 60s 105 | 106 | getStatus 107 | status="$(echo "${workloadStatus}" | jq '.status')" 108 | echo "" 109 | echo "${status}" 110 | echo "" 111 | 112 | numberDesired="$(echo "${status}" | jq -r '.desiredNumberScheduled')" 113 | numberReady="$(echo "${status}" | jq -r '.numberReady')" 114 | 115 | if (( numberReady == numberDesired )); then 116 | echo "Rollback to ${ROLLBACK_IMAGE} completed." 117 | else 118 | echo "FATAL - rollback failed" 119 | fi 120 | echo "Exiting Build..." 121 | exit 1 122 | fi 123 | 124 | else 125 | echo "This TAG is not in a valid format..." 126 | echo "Exiting Build..." 127 | exit 0 128 | fi 129 | echo "Exiting Build..." 130 | exit 0 131 | env: 132 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 133 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 134 | TAG: ${{ github.event.inputs.tag }} 135 | -------------------------------------------------------------------------------- /.github/workflows/maven-pulls.yml: -------------------------------------------------------------------------------- 1 | name: Build Test PR 2 | 3 | on: 4 | pull_request: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | java: [ 11 ] 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Java 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: ${{ matrix.java }} 21 | - name: Cache local Maven repository 22 | uses: actions/cache@v2 23 | with: 24 | path: ~/.m2/repository 25 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 26 | restore-keys: | 27 | ${{ runner.os }}-maven- 28 | - name: Build with Maven and Gradle 29 | run: | 30 | mvn --no-transfer-progress -B install --file pom.xml 31 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Build Test Deploy master 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | java: [ 11 ] 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Java 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: ${{ matrix.java }} 21 | server-id: ossrh 22 | server-username: MAVEN_USERNAME 23 | server-password: MAVEN_PASSWORD 24 | - name: Cache local Maven repository 25 | uses: actions/cache@v2 26 | with: 27 | path: ~/.m2/repository 28 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 29 | restore-keys: | 30 | ${{ runner.os }}-maven- 31 | - name: Build with Maven, Deploy snapshot to maven central 32 | run: | 33 | export MY_POM_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 34 | echo "POM VERSION" ${MY_POM_VERSION} 35 | if [[ $MY_POM_VERSION =~ ^.*SNAPSHOT$ ]]; 36 | then 37 | mvn --no-transfer-progress -B install --file pom.xml 38 | export MY_JAVA_VERSION=`java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1` 39 | echo "JAVA VERSION" ${MY_JAVA_VERSION} 40 | if [[ ${MY_JAVA_VERSION} == "11" ]]; 41 | then 42 | export MY_POM_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 43 | echo "POM VERSION" ${MY_POM_VERSION} 44 | mvn --no-transfer-progress -B clean deploy 45 | else 46 | echo "not deploying on java version: " ${MY_JAVA_VERSION} 47 | fi 48 | else 49 | echo "not building and maven publishing project as it is a release version: " ${MY_JAVA_VERSION} 50 | fi 51 | env: 52 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 53 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 54 | -------------------------------------------------------------------------------- /.github/workflows/prepare-release.yml: -------------------------------------------------------------------------------- 1 | name: Prepare Release 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: ["master"] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: tibdex/github-app-token@v1 15 | id: generate-token 16 | with: 17 | app_id: ${{ secrets.APP_ID }} 18 | private_key: ${{ secrets.APP_PRIVATE_KEY }} 19 | - name: Set up Python 3.10 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: '3.10' 23 | - name: Set up Java 11 24 | uses: actions/setup-java@v1 25 | with: 26 | java-version: 11 27 | server-id: ossrh 28 | server-username: MAVEN_USERNAME 29 | server-password: MAVEN_PASSWORD 30 | - name: Cache local Maven repository 31 | uses: actions/cache@v2 32 | with: 33 | path: ~/.m2/repository 34 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 35 | restore-keys: | 36 | ${{ runner.os }}-maven- 37 | - name: Run prepare release script 38 | id: prepare-release 39 | run: | 40 | export MY_POM_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 41 | if [[ $MY_POM_VERSION =~ ^.*SNAPSHOT$ ]]; 42 | then 43 | . ./CI/prepare-release.sh 44 | echo "PREPARE_RELEASE_OK=yes" >> $GITHUB_ENV 45 | else 46 | echo "not preparing release for release version: " ${MY_POM_VERSION} 47 | echo "PREPARE_RELEASE_OK=no" >> $GITHUB_ENV 48 | fi 49 | echo "SC_VERSION=$SC_VERSION" >> $GITHUB_ENV 50 | echo "SC_NEXT_VERSION=$SC_NEXT_VERSION" >> $GITHUB_ENV 51 | - name: Create Prepare Release Pull Request 52 | uses: peter-evans/create-pull-request@v4 53 | if: env.PREPARE_RELEASE_OK == 'yes' 54 | with: 55 | token: ${{ steps.generate-token.outputs.token }} 56 | commit-message: prepare release ${{ env.SC_VERSION }} 57 | title: 'prepare release ${{ env.SC_VERSION }}' 58 | branch: prepare-release-${{ env.SC_VERSION }} 59 | env: 60 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 61 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 62 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 63 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 64 | SC_VERSION: 65 | SC_NEXT_VERSION: 66 | 67 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: ["master"] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: tibdex/github-app-token@v1 15 | id: generate-token 16 | with: 17 | app_id: ${{ secrets.APP_ID }} 18 | private_key: ${{ secrets.APP_PRIVATE_KEY }} 19 | - name: Set up Python 3.10 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: '3.10' 23 | - name: Set up Java 11 24 | uses: actions/setup-java@v1 25 | with: 26 | java-version: 11 27 | server-id: ossrh 28 | server-username: MAVEN_USERNAME 29 | server-password: MAVEN_PASSWORD 30 | - name: Cache local Maven repository 31 | uses: actions/cache@v2 32 | with: 33 | path: ~/.m2/repository 34 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 35 | restore-keys: | 36 | ${{ runner.os }}-maven- 37 | - name: Run pre release script 38 | id: preRelease 39 | run: | 40 | # export GPG_TTY=$(tty) 41 | export MY_POM_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 42 | if [[ $MY_POM_VERSION =~ ^.*SNAPSHOT$ ]]; 43 | then 44 | echo "not releasing snapshot version: " ${MY_POM_VERSION} 45 | echo "RELEASE_OK=no" >> $GITHUB_ENV 46 | else 47 | . ./CI/pre-release.sh 48 | echo "RELEASE_OK=yes" >> $GITHUB_ENV 49 | fi 50 | echo "SC_VERSION=$SC_VERSION" >> $GITHUB_ENV 51 | echo "SC_NEXT_VERSION=$SC_NEXT_VERSION" >> $GITHUB_ENV 52 | echo "SC_LAST_RELEASE=$SC_LAST_RELEASE" >> $GITHUB_ENV 53 | - name: configure git user email 54 | run: | 55 | git config --global user.email "action@github.com" 56 | git config --global user.name "GitHub Action" 57 | git config --global hub.protocol https 58 | git remote set-url origin https://\${{ secrets.GITHUB_TOKEN }}:x-oauth-basic@github.com/swagger-api/swagger-converter.git 59 | - name: Run maven deploy/release (action-maven-publish) 60 | uses: samuelmeuli/action-maven-publish@v1 61 | if: env.RELEASE_OK == 'yes' 62 | with: 63 | gpg_private_key: ${{ secrets.OSSRH_GPG_PRIVATE_KEY }} 64 | gpg_passphrase: ${{ secrets.OSSRH_GPG_PRIVATE_PASSPHRASE }} 65 | nexus_username: ${{ secrets.OSSRH_USERNAME }} 66 | nexus_password: ${{ secrets.OSSRH_TOKEN }} 67 | maven_profiles: "release" 68 | - name: docker login 69 | run: | 70 | docker login --username=${{ secrets.DOCKERHUB_SB_USERNAME }} --password=${{ secrets.DOCKERHUB_SB_PASSWORD }} 71 | set -e 72 | - name: Docker build and push 73 | id: docker_build_push 74 | if: env.RELEASE_OK == 'yes' 75 | run: | 76 | . ./CI/docker-release.sh 77 | - name: Run post release script 78 | id: postRelease 79 | if: env.RELEASE_OK == 'yes' 80 | run: | 81 | . ./CI/post-release.sh 82 | - name: Create Next Snapshot Pull Request 83 | uses: peter-evans/create-pull-request@v4 84 | if: env.RELEASE_OK == 'yes' 85 | with: 86 | token: ${{ steps.generate-token.outputs.token }} 87 | commit-message: bump snapshot ${{ env.SC_NEXT_VERSION }}-SNAPSHOT 88 | title: 'bump snapshot ${{ env.SC_NEXT_VERSION }}-SNAPSHOT' 89 | branch: bump-snap-${{ env.SC_NEXT_VERSION }}-SNAPSHOT 90 | - name: deploy docker 91 | run: | 92 | SC_RELEASE_TAG="v${{ env.SC_VERSION }}" 93 | echo "$SC_RELEASE_TAG" 94 | 95 | TOKEN="${{ secrets.RANCHER2_BEARER_TOKEN }}" 96 | RANCHER_HOST="rancher.tools.swagger.io" 97 | CLUSTER_ID="c-n8zp2" 98 | NAMESPACE_NAME="swagger-oss" 99 | K8S_OBJECT_TYPE="daemonsets" 100 | K8S_OBJECT_NAME="swagger-converter" 101 | DEPLOY_IMAGE="swaggerapi/swagger-converter:$SC_RELEASE_TAG" 102 | 103 | workloadStatus="" 104 | getStatus() { 105 | echo "Getting update status..." 106 | if ! workloadStatus="$(curl -s -X GET \ 107 | -H "Authorization: Bearer ${TOKEN}" \ 108 | -H 'Content-Type: application/json' \ 109 | "https://${RANCHER_HOST}/k8s/clusters/${CLUSTER_ID}/apis/apps/v1/namespaces/${NAMESPACE_NAME}/${K8S_OBJECT_TYPE}/${K8S_OBJECT_NAME}/status")" 110 | then 111 | echo 'ERROR - get status k8s API call failed!' 112 | echo "Exiting build"... 113 | exit 1 114 | fi 115 | } 116 | 117 | # $1 = image to deploy 118 | updateObject() { 119 | local image="${1}" 120 | echo "Updating image value..." 121 | 122 | if ! curl -s -X PATCH \ 123 | -H "Authorization: Bearer ${TOKEN}" \ 124 | -H 'Content-Type: application/json-patch+json' \ 125 | "https://${RANCHER_HOST}/k8s/clusters/${CLUSTER_ID}/apis/apps/v1/namespaces/${NAMESPACE_NAME}/${K8S_OBJECT_TYPE}/${K8S_OBJECT_NAME}" \ 126 | -d "[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/image\", \"value\": \"${image}\"}]" 127 | then 128 | echo 'ERROR - image update k8s API call failed!' 129 | echo "Exiting build..." 130 | exit 1 131 | fi 132 | } 133 | 134 | 135 | # Check that the TAG is valid 136 | if [[ $SC_RELEASE_TAG =~ ^[vV]?[0-9]*\.[0-9]*\.[0-9]*$ ]]; then 137 | echo "" 138 | echo "This is a Valid TAG..." 139 | 140 | # Get current image/tag in case we need to rollback 141 | getStatus 142 | ROLLBACK_IMAGE="$(echo "${workloadStatus}" | jq -r '.spec.template.spec.containers[0].image')" 143 | echo "" 144 | echo "Current image: ${ROLLBACK_IMAGE}" 145 | 146 | # Update image and validate response 147 | echo "" 148 | updateObject "${DEPLOY_IMAGE}" 149 | echo "" 150 | 151 | echo "" 152 | echo "Waiting for pods to start..." 153 | echo "" 154 | sleep 60s 155 | 156 | # Get state of the k8s object. If numberReady == desiredNumberScheduled, consider the upgrade successful. Else raise error 157 | getStatus 158 | status="$(echo "${workloadStatus}" | jq '.status')" 159 | echo "" 160 | echo "${status}" 161 | echo "" 162 | 163 | numberDesired="$(echo "${status}" | jq -r '.desiredNumberScheduled')" 164 | numberReady="$(echo "${status}" | jq -r '.numberReady')" 165 | 166 | if (( numberReady == numberDesired )); then 167 | echo "${K8S_OBJECT_NAME} has been upgraded to ${DEPLOY_IMAGE}" 168 | 169 | # If pods are not starting, rollback the upgrade and exit the build with error 170 | else 171 | echo "state = error...rolling back upgrade" 172 | updateObject "${ROLLBACK_IMAGE}" 173 | echo "" 174 | 175 | echo "" 176 | echo "Waiting for rollback pods to start..." 177 | echo "" 178 | sleep 60s 179 | 180 | getStatus 181 | status="$(echo "${workloadStatus}" | jq '.status')" 182 | echo "" 183 | echo "${status}" 184 | echo "" 185 | 186 | numberDesired="$(echo "${status}" | jq -r '.desiredNumberScheduled')" 187 | numberReady="$(echo "${status}" | jq -r '.numberReady')" 188 | 189 | if (( numberReady == numberDesired )); then 190 | echo "Rollback to ${ROLLBACK_IMAGE} completed." 191 | else 192 | echo "FATAL - rollback failed" 193 | fi 194 | echo "Exiting Build..." 195 | exit 1 196 | fi 197 | 198 | else 199 | echo "This TAG is not in a valid format..." 200 | echo "Exiting Build..." 201 | exit 0 202 | fi 203 | echo "Exiting Build..." 204 | exit 0 205 | env: 206 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 207 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 208 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 209 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 210 | SC_VERSION: 211 | SC_NEXT_VERSION: 212 | GPG_PRIVATE_KEY: ${{ secrets.OSSRH_GPG_PRIVATE_KEY }} 213 | GPG_PASSPHRASE: ${{ secrets.OSSRH_GPG_PRIVATE_PASSPHRASE }} 214 | GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} 215 | GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} 216 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | lib/*.jar 3 | target 4 | samples/scala-play2/logs 5 | .idea 6 | .idea_modules 7 | .settings 8 | .project 9 | .classpath 10 | .cache 11 | atlassian-ide-plugin.xml 12 | *.iml 13 | .java-version 14 | sonar-project.properties 15 | *.ipr 16 | *.iws 17 | .DS_Store 18 | dependency-reduced-pom.xml 19 | *.pyc 20 | /bin/ 21 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "settingsInheritedFrom": "swagger-api/whitesource-config@main" 3 | } 4 | -------------------------------------------------------------------------------- /CI/CI.md: -------------------------------------------------------------------------------- 1 | ## Continuous integration 2 | 3 | ### Build, test and deploy 4 | Swagger Validator uses Github actions to run jobs/checks building, testing and deploying snapshots on push and PR events. 5 | 6 | These github actions are configured in `.github/workflows`: 7 | 8 | * maven.yml : Build Test Deploy master 9 | * maven-pulls.yml Build Test PR 10 | 11 | 12 | These actions use available actions in combination with short bash scripts. 13 | 14 | ### Release 15 | 16 | Releases are semi-automated and consist in 2 actions using available public actions in combination with bash and python scripts. 17 | **TODO**: Python code is used for historical reasons to execute GitHub APIs calls, in general a more consistent environment would 18 | be more maintainable e.g. implementing a custom JavaScript or Docker Container GitHub Action and/or a bash only script(s). 19 | 20 | #### Workflow summary 21 | 22 | 1. execute `prepare-release.yml` / `Prepare Release` for `master` branch 23 | 1. check and merge the Prepare Release PR pushed by previous step. Delete the branch 24 | 1. execute `release.yml` / `Release` for `master` branch 25 | 1. check and merge the next snaphot PR pushed by previous step. Delete the branch 26 | 27 | #### Prepare Release 28 | 29 | The first action to execute is `prepare-release.yml` / `Prepare Release` 30 | 31 | This is triggered by manually executing the action, selecting `Actions` in project GitHub UI, then `Prepare Release` workflow 32 | and clicking `Run Workflow` 33 | 34 | `Prepare Release` takes care of: 35 | 36 | * create release notes out of merged PRs 37 | * Draft a release with related tag 38 | * bump versions to release, and update all affected files 39 | * build and test maven 40 | * push a Pull Request with the changes for human check. 41 | 42 | After the PR checks complete, the PR can me merged, and the second phase `Release` started. 43 | 44 | #### Release 45 | 46 | Once prepare release PR has been merged, the second phase is provided by `release.yml` / `Release` actions. 47 | 48 | This is triggered by manually executing the action, selecting `Actions` in project GitHub UI, then `Release` workflow 49 | and clicking `Run Workflow` 50 | 51 | `Release` takes care of: 52 | 53 | * build and test maven 54 | * deploy/publish to maven central 55 | * publish the previously prepared GitHub release / tag 56 | * build and push docker image 57 | * deploy/publish docker image to docker hub 58 | * push PR for next snapshot 59 | 60 | 61 | 62 | ### Secrets 63 | 64 | GitHub Actions make use of `Secrets` which can be configured either with Repo or Organization scope; the needed secrets are the following: 65 | 66 | * `APP_ID` and APP_PRIVATE_KEY`: these are the values provided by an account configured GitHub App, allowing to obtain a GitHub token 67 | different from the default used in GitHub Actions (which does not allow to "chain" actions).Actions 68 | 69 | The GitHub App must be configured as detailed in [this doc](https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens). 70 | 71 | See also [here](https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#triggering-further-workflow-runs) 72 | 73 | * `OSSRH_GPG_PRIVATE_KEY` and `OSSRH_GPG_PRIVATE_PASSPHRASE` : gpg key and passphrase to be used for sonatype releases 74 | GPG private key and passphrase defined to be used for sonatype deployments, as detailed in 75 | https://central.sonatype.org/pages/working-with-pgp-signatures.html (I'd say with email matching the one of the sonatype account of point 1 76 | 77 | * `OSSRH_USERNAME` and `OSSRH_TOKEN`: sonatype user/token 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /CI/docker-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUR=$(pwd) 4 | 5 | SC_RELEASE_TAG="v$SC_VERSION" 6 | 7 | echo "docker tag:" 8 | echo "$SC_RELEASE_TAG" 9 | 10 | export DOCKER_CONVERTER_IMAGE_NAME=swaggerapi/swagger-converter 11 | docker build --rm=false -t $DOCKER_CONVERTER_IMAGE_NAME:$SC_RELEASE_TAG . 12 | docker tag $DOCKER_CONVERTER_IMAGE_NAME:$SC_RELEASE_TAG $DOCKER_CONVERTER_IMAGE_NAME:latest 13 | docker push $DOCKER_CONVERTER_IMAGE_NAME:$SC_RELEASE_TAG 14 | docker push $DOCKER_CONVERTER_IMAGE_NAME:latest 15 | echo "docker images:" 16 | docker images | grep -i converter 17 | -------------------------------------------------------------------------------- /CI/ghApiClient.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import time 5 | import urllib.request, urllib.error, urllib.parse 6 | import http.client 7 | import json 8 | 9 | GH_BASE_URL = "https://api.github.com/" 10 | 11 | GH_TOKEN = os.environ['GH_TOKEN'] 12 | GH_AUTH = "Bearer %s" % GH_TOKEN 13 | 14 | def readUrl(name): 15 | try: 16 | request = urllib.request.Request(GH_BASE_URL + name) 17 | request.add_header("Authorization", GH_AUTH) 18 | content = urllib.request.urlopen(request).read() 19 | jcont = json.loads(content) 20 | return jcont 21 | except urllib.error.HTTPError as e: 22 | print(('HTTPError = ' + str(e.code))) 23 | raise e 24 | except urllib.error.URLError as e: 25 | print(('URLError = ' + str(e.reason))) 26 | raise e 27 | except http.client.HTTPException as e: 28 | print(('HTTPException = ' + str(e))) 29 | raise e 30 | except Exception: 31 | import traceback 32 | print(('generic exception: ' + traceback.format_exc())) 33 | raise IOError 34 | 35 | def postUrl(name, body): 36 | global GH_BASE_URL 37 | try: 38 | time.sleep(0.05) 39 | request = urllib.request.Request(GH_BASE_URL + name) 40 | request.add_header("Authorization", GH_AUTH) 41 | request.add_header("Accept", "application/vnd.github.v3+json") 42 | data = body.encode('utf-8') 43 | content = urllib.request.urlopen(request, data).read() 44 | jcont = json.loads(content) 45 | return jcont 46 | except urllib.error.HTTPError as e: 47 | print(('HTTPError = ' + str(e.code))) 48 | print((str(e))) 49 | raise e 50 | except urllib.error.URLError as e: 51 | print(('URLError = ' + str(e.reason))) 52 | raise e 53 | except http.client.HTTPException as e: 54 | print(('HTTPException = ' + str(e))) 55 | raise e 56 | except Exception: 57 | import traceback 58 | print(('generic exception: ' + traceback.format_exc())) 59 | raise IOError 60 | -------------------------------------------------------------------------------- /CI/lastRelease.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import ghApiClient 4 | 5 | def getLastReleaseTag(): 6 | content = ghApiClient.readUrl('repos/swagger-api/swagger-converter/releases') 7 | for l in content: 8 | draft = l["draft"] 9 | tag = l["tag_name"] 10 | if str(draft) != 'True' and tag.startswith("v1"): 11 | return tag[1:] 12 | return "1.0.3" 13 | # main 14 | def main(): 15 | result = getLastReleaseTag() 16 | print (result) 17 | 18 | # here start main 19 | main() 20 | -------------------------------------------------------------------------------- /CI/post-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUR=$(pwd) 4 | TMPDIR="$(dirname -- "${0}")" 5 | 6 | SC_RELEASE_TAG="v$SC_VERSION" 7 | 8 | ##################### 9 | ### publish pre-prepared release (tag is created) 10 | ##################### 11 | python $CUR/CI/publishRelease.py "$SC_RELEASE_TAG" 12 | 13 | ##################### 14 | ### update the version to next snapshot in maven project with set version 15 | ##################### 16 | mvn versions:set -DnewVersion="${SC_NEXT_VERSION}-SNAPSHOT" 17 | mvn versions:commit 18 | 19 | ##################### 20 | ### update all other versions in files around to the next snapshot or new release, including readme and gradle ### 21 | ##################### 22 | 23 | sc_find="version\: $SC_VERSION" 24 | sc_replace="version: $SC_NEXT_VERSION-SNAPSHOT" 25 | sed -i -e "s/$sc_find/$sc_replace/g" $CUR/src/main/swagger/swagger.yaml 26 | 27 | sc_find="swagger\-converter\:v$SC_VERSION" 28 | sc_replace="swagger-converter:v$SC_NEXT_VERSION-SNAPSHOT" 29 | sed -i -e "s/$sc_find/$sc_replace/g" $CUR/README.md 30 | -------------------------------------------------------------------------------- /CI/pre-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUR=$(pwd) 4 | 5 | export SC_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}' --non-recursive build-helper:parse-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 6 | export SC_NEXT_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.nextIncrementalVersion}' --non-recursive build-helper:parse-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 7 | SC_QUALIFIER=`mvn -q -Dexec.executable="echo" -Dexec.args='${parsedVersion.qualifier}' --non-recursive build-helper:parse-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 8 | #SC_LAST_RELEASE=`mvn -q -Dexec.executable="echo" -Dexec.args='${releasedVersion.version}' --non-recursive org.codehaus.mojo:build-helper-maven-plugin:3.2.0:released-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 9 | SC_LAST_RELEASE=`python $CUR/CI/lastRelease.py` 10 | 11 | 12 | SC_RELEASE_TAG="v$SC_VERSION" 13 | 14 | 15 | ##################### 16 | ### build and test maven ### 17 | ##################### 18 | mvn --no-transfer-progress -B install --file pom.xml 19 | -------------------------------------------------------------------------------- /CI/prepare-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUR=$(pwd) 4 | 5 | export SC_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}' --non-recursive build-helper:parse-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 6 | export SC_NEXT_VERSION=`mvn -q -Dexec.executable="echo" -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.nextIncrementalVersion}' --non-recursive build-helper:parse-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 7 | SC_QUALIFIER=`mvn -q -Dexec.executable="echo" -Dexec.args='${parsedVersion.qualifier}' --non-recursive build-helper:parse-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 8 | #SC_LAST_RELEASE=`mvn -q -Dexec.executable="echo" -Dexec.args='${releasedVersion.version}' --non-recursive org.codehaus.mojo:build-helper-maven-plugin:3.2.0:released-version org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` 9 | SC_LAST_RELEASE=`python $CUR/CI/lastRelease.py` 10 | 11 | 12 | 13 | SC_RELEASE_TITLE="Swagger Converter $SC_VERSION released!" 14 | SC_RELEASE_TAG="v$SC_VERSION" 15 | 16 | echo "SC_VERSION: $SC_VERSION" 17 | echo "SC_NEXT_VERSION: $SC_NEXT_VERSION" 18 | echo "SC_LAST_RELEASE: $SC_LAST_RELEASE" 19 | echo "SC_RELEASE_TITLE: $SC_RELEASE_TITLE" 20 | echo "SC_RELEASE_TAG: $SC_RELEASE_TAG" 21 | 22 | ##################### 23 | ### draft release Notes with next release after last release, with tag 24 | ##################### 25 | python $CUR/CI/releaseNotes.py "$SC_LAST_RELEASE" "$SC_RELEASE_TITLE" "$SC_RELEASE_TAG" 26 | 27 | ##################### 28 | ### update the version to release in maven project with set version 29 | ##################### 30 | mvn versions:set -DnewVersion=$SC_VERSION 31 | mvn versions:commit 32 | 33 | 34 | ##################### 35 | ### update all other versions in files around to the new release 36 | ##################### 37 | sc_find="swagger\-converter\:v$SC_LAST_RELEASE" 38 | sc_replace="swagger-converter:v$SC_VERSION" 39 | sed -i -e "s/$sc_find/$sc_replace/g" $CUR/README.md 40 | 41 | 42 | sc_find="version\: $SC_VERSION\-SNAPSHOT" 43 | sc_replace="version: $SC_VERSION" 44 | sed -i -e "s/$sc_find/$sc_replace/g" $CUR/src/main/swagger/swagger.yaml 45 | 46 | 47 | 48 | ##################### 49 | ### build and test maven ### 50 | ##################### 51 | mvn --no-transfer-progress -B install --file pom.xml 52 | 53 | -------------------------------------------------------------------------------- /CI/publishRelease.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import ghApiClient 5 | 6 | def lastReleaseId(tag): 7 | content = ghApiClient.readUrl('repos/swagger-api/swagger-converter/releases') 8 | for l in content: 9 | draft = l["draft"] 10 | draft_tag = l["tag_name"] 11 | if str(draft) == 'True' and tag == draft_tag: 12 | return l["id"] 13 | 14 | def publishRelease(tag): 15 | id = lastReleaseId(tag) 16 | payload = "{\"tag_name\":\"" + tag + "\", " 17 | payload += "\"draft\":" + "false" + ", " 18 | payload += "\"target_commitish\":\"" + "master" + "\"}" 19 | content = ghApiClient.postUrl('repos/swagger-api/swagger-converter/releases/' + str(id), payload) 20 | return content 21 | 22 | # main 23 | def main(tag): 24 | publishRelease (tag) 25 | 26 | # here start main 27 | main(sys.argv[1]) 28 | -------------------------------------------------------------------------------- /CI/releaseNotes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import json 5 | from datetime import datetime 6 | import ghApiClient 7 | 8 | def allPulls(releaseDate): 9 | 10 | result = "" 11 | 12 | baseurl = "https://api.github.com/repos/swagger-api/swagger-converter/pulls/" 13 | content = ghApiClient.readUrl('repos/swagger-api/swagger-converter/pulls?state=closed&base=master&per_page=100') 14 | for l in content: 15 | stripped = l["url"][len(baseurl):] 16 | mergedAt = l["merged_at"] 17 | if mergedAt is not None: 18 | if datetime.strptime(mergedAt, '%Y-%m-%dT%H:%M:%SZ') > releaseDate: 19 | if not l['title'].startswith("bump snap"): 20 | result += '\n' 21 | result += "* " + l['title'] + " (#" + stripped + ")" 22 | return result 23 | 24 | 25 | def lastReleaseDate(tag): 26 | content = ghApiClient.readUrl('repos/swagger-api/swagger-converter/releases/tags/' + tag) 27 | publishedAt = content["published_at"] 28 | return datetime.strptime(publishedAt, '%Y-%m-%dT%H:%M:%SZ') 29 | 30 | 31 | def addRelease(release_title, tag, content): 32 | payload = "{\"tag_name\":\"" + tag + "\", " 33 | payload += "\"name\":" + json.dumps(release_title) + ", " 34 | payload += "\"body\":" + json.dumps(content) + ", " 35 | payload += "\"draft\":" + "true" + ", " 36 | payload += "\"prerelease\":" + "false" + ", " 37 | payload += "\"target_commitish\":\"" + "master" + "\"}" 38 | content = ghApiClient.postUrl('repos/swagger-api/swagger-converter/releases', payload) 39 | return content 40 | 41 | def getReleases(): 42 | content = ghApiClient.readUrl('repos/swagger-api/swagger-converter/releases') 43 | return content 44 | 45 | # main 46 | def main(last_release, release_title, tag): 47 | result = allPulls(lastReleaseDate('v' + last_release)) 48 | addRelease (release_title, tag, result) 49 | 50 | # here start main 51 | main(sys.argv[1], sys.argv[2], sys.argv[3]) 52 | 53 | -------------------------------------------------------------------------------- /CI/version.sh: -------------------------------------------------------------------------------- 1 | grep version pom.xml | grep -v -e '//g' | awk '{print $1}' -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre-alpine 2 | 3 | WORKDIR /swagger-converter 4 | 5 | COPY target/lib/jetty-runner.jar /swagger-converter/jetty-runner.jar 6 | COPY target/*.war /swagger-converter/server.war 7 | COPY src/main/swagger/swagger.yaml /swagger-converter/ 8 | COPY inflector.yaml /swagger-converter/ 9 | 10 | EXPOSE 8080 11 | 12 | CMD ["java", "-jar", "-DswaggerUrl=swagger.yaml", "/swagger-converter/jetty-runner.jar", "/swagger-converter/server.war"] 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright (c) 2015. SmartBear Software Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Swagger Converter - ${pom.name} 2 | Copyright (c) 2015. SmartBear Software Inc. 3 | Swagger Converter - ${pom.name} is licensed under Apache 2.0 license. 4 | Copy of the Apache 2.0 license can be found in `LICENSE` file. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Provides a converter between OpenAPI 2.0 and 3.0 specifications accessible via APIs and web UI. 2 | 3 | It is available online at https://converter.swagger.io/ and as a [docker image](https://hub.docker.com/r/swaggerapi/swagger-converter) e.g.: 4 | 5 | ``` 6 | docker pull swaggerapi/swagger-converter:v1.0.6-SNAPSHOT 7 | docker run -it -p 8080:8080 --name swagger-converter swaggerapi/swagger-converter:v1.0.6-SNAPSHOT 8 | ``` 9 | 10 | Web UI is reachable at http://localhost:8080/index.html and OpenAPI spec at http://localhost:8080/api/openapi.json 11 | 12 | ## Security contact 13 | 14 | Please disclose any security-related issues or vulnerabilities by emailing [security@swagger.io](mailto:security@swagger.io), instead of using the public issue tracker. 15 | 16 | ## License 17 | 18 | ``` 19 | Copyright 2019 SmartBear Software 20 | 21 | Licensed under the Apache License, Version 2.0 (the "License"); 22 | you may not use this file except in compliance with the License. 23 | You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | 25 | Unless required by applicable law or agreed to in writing, software 26 | distributed under the License is distributed on an "AS IS" BASIS, 27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | See the License for the specific language governing permissions and 29 | limitations under the License. 30 | ``` 31 | -------------------------------------------------------------------------------- /inflector.yaml: -------------------------------------------------------------------------------- 1 | controllerPackage: io.swagger.handler 2 | modelPackage: io.swagger.v3.core.model 3 | swaggerUrl: ./src/main/swagger/swagger.yaml 4 | 5 | entityProcessors: 6 | - json 7 | - yaml 8 | 9 | rootPath: /api -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.sonatype.oss 4 | oss-parent 5 | 5 6 | 7 | 4.0.0 8 | io.swagger 9 | swagger-converter 10 | war 11 | swagger-converter 12 | 1.0.6-SNAPSHOT 13 | 14 | 2.2.0 15 | 16 | https://github.com/swagger-api/swagger-converter 17 | 18 | 19 | ossrh 20 | https://oss.sonatype.org/content/repositories/snapshots 21 | 22 | 23 | ossrh 24 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 25 | 26 | 27 | 28 | 29 | Apache License, Version 2.0 30 | http://www.apache.org/licenses/LICENSE-2.0.txt 31 | repo 32 | 33 | 34 | 35 | github 36 | https://github.com/swagger-api/swagger-converter/issues 37 | 38 | 39 | scm:git:git://github.com/swagger-api/swagger-converter.git 40 | scm:git:ssh://github.com:swagger-api/swagger-converter.git 41 | http://github.com/HugoMario/swagger-converter/tree/main 42 | 43 | 44 | 45 | fehguy 46 | Tony Tam 47 | fehguy@gmail.com 48 | 49 | 50 | 51 | install 52 | target 53 | ${project.artifactId}-${project.version} 54 | 55 | 56 | src/main/resources 57 | 58 | 59 | META-INF 60 | ../.. 61 | true 62 | 63 | NOTICE 64 | LICENSE 65 | 66 | 67 | 68 | 69 | 70 | org.jvnet.wagon-svn 71 | wagon-svn 72 | 1.12 73 | 74 | 75 | org.apache.maven.wagon 76 | wagon-ssh-external 77 | 3.5.3 78 | 79 | 80 | org.apache.maven.wagon 81 | wagon-webdav 82 | 1.0-beta-2 83 | 84 | 85 | 86 | 87 | maven-compiler-plugin 88 | 3.11.0 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-source-plugin 93 | 3.3.0 94 | 95 | 96 | attach-sources 97 | verify 98 | 99 | jar-no-fork 100 | 101 | 102 | 103 | 104 | 105 | maven-dependency-plugin 106 | 107 | 108 | package 109 | 110 | copy-dependencies 111 | copy 112 | 113 | 114 | ${project.build.directory}/lib 115 | 116 | 117 | org.eclipse.jetty 118 | jetty-runner 119 | ${jetty-version} 120 | jetty-runner.jar 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-jar-plugin 130 | 2.6 131 | 132 | 133 | **/logback.xml 134 | 135 | 136 | 137 | development 138 | ${project.url} 139 | ${project.version} 140 | io.swagger 141 | 142 | 143 | 144 | 145 | 146 | org.eclipse.jetty 147 | jetty-maven-plugin 148 | ${jetty-version} 149 | 150 | 151 | inflector.yaml 152 | src/main/swagger/swagger.yaml 153 | 154 | 1 155 | 156 | / 157 | 158 | target/${project.artifactId}-${project.version} 159 | 160 | 8080 161 | 60000 162 | 163 | 164 | 165 | 166 | com.googlecode.maven-download-plugin 167 | download-maven-plugin 168 | 1.7.1 169 | 170 | 171 | swagger-ui 172 | validate 173 | 174 | wget 175 | 176 | 177 | true 178 | https://github.com/swagger-api/swagger-ui/archive/master.tar.gz 179 | true 180 | ${project.build.directory} 181 | 182 | 183 | 184 | 185 | 186 | maven-resources-plugin 187 | 2.6 188 | 189 | 190 | copy-resources 191 | prepare-package 192 | 193 | copy-resources 194 | 195 | 196 | target/${project.artifactId}-${project.version} 197 | 198 | 199 | ${project.build.directory}/swagger-ui-master/dist 200 | true 201 | 202 | index.html 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-javadoc-plugin 213 | 3.6.2 214 | 215 | true 216 | 1.8 217 | UTF-8 218 | 1g 219 | 220 | http://docs.oracle.com/javase/8/docs/api 221 | 222 | ${javadoc.package.exclude} 223 | 224 | 225 | 226 | attach-javadocs 227 | verify 228 | 229 | jar 230 | 231 | 232 | 233 | 234 | 235 | org.sonatype.plugins 236 | nexus-staging-maven-plugin 237 | 1.6.13 238 | true 239 | 240 | ossrh 241 | https://oss.sonatype.org/ 242 | true 243 | 30 244 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | org.apache.maven.plugins 254 | maven-gpg-plugin 255 | 1.6 256 | 257 | 258 | 259 | --pinentry-mode 260 | loopback 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | release 270 | 271 | 272 | 273 | org.apache.maven.plugins 274 | maven-gpg-plugin 275 | 1.6 276 | 277 | 278 | 279 | --pinentry-mode 280 | loopback 281 | 282 | 283 | 284 | 285 | sign-artifacts 286 | verify 287 | 288 | sign 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | io.swagger 301 | swagger-inflector 302 | ${swagger-inflector-version} 303 | 304 | 305 | 306 | io.swagger.parser.v3 307 | swagger-parser 308 | ${swagger-parser-version} 309 | 310 | 311 | 312 | com.fasterxml.jackson.jaxrs 313 | jackson-jaxrs-yaml-provider 314 | 2.15.3 315 | 316 | 317 | 318 | 319 | 320 | sonatype-snapshots 321 | https://oss.sonatype.org/content/repositories/snapshots 322 | 323 | true 324 | 325 | 326 | 327 | 328 | 8 329 | 1.0.0 330 | 2.1.22 331 | 2.0.12 332 | 9.4.53.v20231009 333 | 1.5.3 334 | 4.13.2 335 | 2.0.9 336 | UTF-8 337 | https://oss.sonatype.org/content/repositories/snapshots/ 338 | 339 | 340 | -------------------------------------------------------------------------------- /src/main/java/io/swagger/handler/ConverterController.java: -------------------------------------------------------------------------------- 1 | package io.swagger.handler; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import io.swagger.oas.inflector.models.RequestContext; 5 | import io.swagger.oas.inflector.models.ResponseContext; 6 | import io.swagger.parser.OpenAPIParser; 7 | import io.swagger.util.Json; 8 | import io.swagger.v3.parser.core.models.SwaggerParseResult; 9 | 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | 13 | @javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaInflectorServerCodegen", date = "2017-04-08T15:48:56.501Z") 14 | public class ConverterController { 15 | public ResponseContext convertByContent(RequestContext request, JsonNode inputSpec) { 16 | if(inputSpec == null) { 17 | return new ResponseContext() 18 | .status(Response.Status.BAD_REQUEST) 19 | .entity( "No specification supplied in either the url or request body. Try again?" ); 20 | } 21 | String inputAsString = Json.pretty(inputSpec); 22 | SwaggerParseResult output = new OpenAPIParser().readContents(inputAsString, null, null); 23 | if(output == null) { 24 | return new ResponseContext().status(Response.Status.INTERNAL_SERVER_ERROR).entity( "Failed to process URL" ); 25 | } 26 | if(output.getOpenAPI() == null) { 27 | return new ResponseContext().status(Response.Status.BAD_REQUEST).entity(output.getMessages()); 28 | } 29 | 30 | 31 | MediaType outputType = getMediaType(request); 32 | 33 | return new ResponseContext() 34 | .contentType(outputType) 35 | .entity(output.getOpenAPI()); 36 | } 37 | 38 | public ResponseContext convertByUrl(RequestContext request , String url) { 39 | if(url == null) { 40 | return new ResponseContext() 41 | .status(Response.Status.BAD_REQUEST) 42 | .entity( "No specification supplied in either the url or request body. Try again?" ); 43 | } 44 | SwaggerParseResult output = new OpenAPIParser().readLocation(url, null, null); 45 | if(output == null) { 46 | return new ResponseContext().status(Response.Status.INTERNAL_SERVER_ERROR).entity( "Failed to process URL" ); 47 | } 48 | if(output.getOpenAPI() == null) { 49 | return new ResponseContext().status(Response.Status.BAD_REQUEST).entity(output.getMessages()); 50 | } 51 | 52 | MediaType outputType = getMediaType(request); 53 | 54 | return new ResponseContext() 55 | .contentType(outputType) 56 | .entity(output.getOpenAPI()); 57 | } 58 | 59 | private MediaType getMediaType(RequestContext request) { 60 | MediaType outputType = MediaType.APPLICATION_JSON_TYPE; 61 | 62 | boolean isJsonOK = false; 63 | boolean isYamlOK = false; 64 | 65 | MediaType yamlMediaType = new MediaType("application", "yaml"); 66 | 67 | for (MediaType mediaType : request.getAcceptableMediaTypes()) { 68 | if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { 69 | isJsonOK = true; 70 | } else if (mediaType.equals(yamlMediaType)) { 71 | isYamlOK = true; 72 | } 73 | } 74 | 75 | if (isYamlOK && !isJsonOK) { 76 | outputType = yamlMediaType; 77 | } 78 | 79 | return outputType; 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /src/main/swagger/swagger.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | openapi: 3.0.0 3 | info: 4 | title: Swagger Converter 5 | description: Converts a 1.x or 2.x Swagger definition to the OpenAPI 3.0.1 format 6 | version: 1.0.6-SNAPSHOT 7 | servers: 8 | - url: "/" 9 | paths: 10 | "/convert": 11 | get: 12 | tags: 13 | - Converter 14 | summary: Convert a swagger definition 15 | description: | 16 | Converts the supplied payload to a 3.0 specification 17 | based on a `url` parameter, which points to a older 18 | specification version 19 | operationId: convertByUrl 20 | parameters: 21 | - name: url 22 | in: query 23 | description: A URL to the swagger definition 24 | required: true 25 | schema: 26 | type: string 27 | responses: 28 | '200': 29 | description: successfully converted schema 30 | content: 31 | "application/json": 32 | schema: 33 | type: object 34 | properties: {} 35 | "application/yaml": 36 | schema: 37 | type: object 38 | properties: {} 39 | '400': 40 | description: 'invaild input schema, or could not be converted' 41 | content: 42 | "*/*": 43 | schema: 44 | type: array 45 | description: error messages 46 | items: 47 | type: string 48 | post: 49 | tags: 50 | - Converter 51 | summary: Convert a swagger definition 52 | description: | 53 | Converts the supplied payload to a 3.0 specification 54 | based on an input specification 55 | operationId: convertByContent 56 | requestBody: 57 | description: the specification to convert 58 | content: 59 | "application/json": 60 | schema: 61 | type: object 62 | properties: {} 63 | "application/yaml": 64 | schema: 65 | type: object 66 | properties: {} 67 | required: true 68 | responses: 69 | '200': 70 | description: successfully converted schema 71 | content: 72 | "application/json": 73 | schema: 74 | type: object 75 | properties: {} 76 | "application/yaml": 77 | schema: 78 | type: object 79 | properties: {} 80 | '400': 81 | description: 'invaild input schema, or could not be converted' 82 | content: 83 | "*/*": 84 | schema: 85 | type: array 86 | description: error messages 87 | items: 88 | type: string 89 | components: {} 90 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | swagger-inflector 5 | org.glassfish.jersey.servlet.ServletContainer 6 | 7 | javax.ws.rs.Application 8 | io.swagger.oas.inflector.OpenAPIInflector 9 | 10 | 11 | jersey.config.server.provider.packages 12 | 13 | com.fasterxml.jackson.jaxrs.yaml.JacksonYAMLProvider 14 | 15 | 16 | 1 17 | 18 | 19 | swagger-inflector 20 | /api/* 21 | 22 | 23 | CORSFilter 24 | io.swagger.oas.inflector.utils.CORSFilter 25 | 26 | 27 | CORSFilter 28 | /* 29 | 30 | -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 11 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 | 71 | 72 | 93 | 94 | 95 | --------------------------------------------------------------------------------