Repository: viclovsky/swagger-coverage Branch: master Commit: 3d420920ce25 Files: 186 Total size: 625.7 KB Directory structure: gitextract_lgq7gab1/ ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE.md │ ├── dependabot.yml │ ├── release-drafter.yml │ └── workflows/ │ ├── build.yml │ ├── gradle-wrapper-validation.yml │ ├── label-verify.yml │ ├── release-draft.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle/ │ ├── maven-publish.gradle │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── run.sh ├── settings.gradle.kts ├── swagger-coverage-commandline/ │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── github/ │ │ │ └── viclovsky/ │ │ │ └── swagger/ │ │ │ └── coverage/ │ │ │ ├── CommandLine.java │ │ │ ├── ExitCode.java │ │ │ ├── configuration/ │ │ │ │ ├── Configuration.java │ │ │ │ ├── ConfigurationBuilder.java │ │ │ │ └── options/ │ │ │ │ ├── ConfigurationOptions.java │ │ │ │ ├── MainOptions.java │ │ │ │ ├── ResultsWriterOptions.java │ │ │ │ └── RuleConfigurationOptions.java │ │ │ ├── core/ │ │ │ │ ├── generator/ │ │ │ │ │ ├── Generator.java │ │ │ │ │ ├── OperationConditionGenerator.java │ │ │ │ │ └── SwaggerSpecificationProcessor.java │ │ │ │ ├── model/ │ │ │ │ │ ├── Condition.java │ │ │ │ │ ├── ConditionOperationCoverage.java │ │ │ │ │ ├── OperationKey.java │ │ │ │ │ ├── OperationsHolder.java │ │ │ │ │ └── SinglePredicateCondition.java │ │ │ │ ├── predicate/ │ │ │ │ │ ├── ConditionPredicate.java │ │ │ │ │ ├── DefaultBodyConditionPredicate.java │ │ │ │ │ ├── DefaultParameterConditionPredicate.java │ │ │ │ │ ├── DefaultParameterValueConditionPredicate.java │ │ │ │ │ ├── DefaultPropertyConditionPredicate.java │ │ │ │ │ ├── DefaultStatusConditionPredicate.java │ │ │ │ │ ├── FullStatusConditionPredicate.java │ │ │ │ │ ├── NotOnlyParameterListValueConditionPredicate.java │ │ │ │ │ ├── ParameterConditionPredicate.java │ │ │ │ │ ├── ParameterUtils.java │ │ │ │ │ ├── ParameterValueConditionPredicate.java │ │ │ │ │ ├── PropertyConditionPredicate.java │ │ │ │ │ ├── PropertyValueConditionPredicate.java │ │ │ │ │ └── PropertyValueNotOnlyConditionPredicate.java │ │ │ │ ├── results/ │ │ │ │ │ ├── Results.java │ │ │ │ │ ├── builder/ │ │ │ │ │ │ ├── core/ │ │ │ │ │ │ │ ├── StatisticsBuilder.java │ │ │ │ │ │ │ ├── StatisticsOperationPostBuilder.java │ │ │ │ │ │ │ ├── StatisticsPostBuilder.java │ │ │ │ │ │ │ └── StatisticsPreBuilder.java │ │ │ │ │ │ ├── postbuilder/ │ │ │ │ │ │ │ ├── ConditionStatisticsBuilder.java │ │ │ │ │ │ │ ├── ConfigurationStatisticsBuilder.java │ │ │ │ │ │ │ ├── FlatOperationBuilder.java │ │ │ │ │ │ │ ├── SwaggerInfoBuilder.java │ │ │ │ │ │ │ ├── TagStatisticsBuilder.java │ │ │ │ │ │ │ └── ZeroCallStatisticsBuilder.java │ │ │ │ │ │ └── prebuilder/ │ │ │ │ │ │ ├── CoverageStatisticsBuilder.java │ │ │ │ │ │ └── GenerationStatisticsBuilder.java │ │ │ │ │ ├── data/ │ │ │ │ │ │ ├── ConditionCounter.java │ │ │ │ │ │ ├── ConditionStatistics.java │ │ │ │ │ │ ├── ConditionStatisticsItem.java │ │ │ │ │ │ ├── CoverageCounter.java │ │ │ │ │ │ ├── CoverageOperationMap.java │ │ │ │ │ │ ├── CoverageState.java │ │ │ │ │ │ ├── GenerationStatistics.java │ │ │ │ │ │ ├── OperationResult.java │ │ │ │ │ │ └── TagCoverage.java │ │ │ │ │ └── util/ │ │ │ │ │ └── DateTimeUtil.java │ │ │ │ ├── rule/ │ │ │ │ │ ├── body/ │ │ │ │ │ │ ├── NotEmptyBodyRule.java │ │ │ │ │ │ ├── PropertyConditionRule.java │ │ │ │ │ │ ├── PropertyEnumAllValuesRule.java │ │ │ │ │ │ ├── PropertyNotEmptyRule.java │ │ │ │ │ │ └── PropertyNotOnlyEnumValuesRule.java │ │ │ │ │ ├── core/ │ │ │ │ │ │ └── ConditionRule.java │ │ │ │ │ ├── parameter/ │ │ │ │ │ │ ├── EmptyHeaderRule.java │ │ │ │ │ │ ├── EnumAllValuesRule.java │ │ │ │ │ │ ├── NotEmptyParameterRule.java │ │ │ │ │ │ ├── NotOnlyEnumValuesRule.java │ │ │ │ │ │ └── ParameterConditionRule.java │ │ │ │ │ └── status/ │ │ │ │ │ ├── HTTPStatusRule.java │ │ │ │ │ ├── OnlyDeclaredHTTPStatusesRule.java │ │ │ │ │ └── StatusConditionRule.java │ │ │ │ └── writer/ │ │ │ │ ├── CoverageResultsWriter.java │ │ │ │ ├── FileSystemResultsWriter.java │ │ │ │ ├── HtmlReportResultsWriter.java │ │ │ │ └── LogResultsWriter.java │ │ │ └── option/ │ │ │ ├── MainOptions.java │ │ │ └── VerboseOptions.java │ │ └── resources/ │ │ └── logback.xml │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── github/ │ │ └── viclovsky/ │ │ └── swagger/ │ │ └── coverage/ │ │ ├── Config.java │ │ ├── CustomReportTemplateTest.java │ │ ├── ParseOptionsTest.java │ │ └── SimpleTest.java │ └── resources/ │ ├── configuration.json │ ├── full_configuration.json │ ├── report_custom.ftl │ ├── v2/ │ │ ├── petstory.json │ │ ├── petstory_no_tags.json │ │ ├── petstory_operation_wo_tags.json │ │ ├── petstory_with_x_example.json │ │ ├── petstory_without_parameters.json │ │ └── swagger-coverage-output/ │ │ ├── empty_parameters.json │ │ ├── enum_param_1.json │ │ ├── enum_param_2.json │ │ ├── ignore_header.json │ │ ├── match_pattern.json │ │ ├── missed_in_swagger.json │ │ ├── missed_in_swagger_without_x_example.json │ │ ├── not_200.json │ │ ├── one_coverage.json │ │ ├── one_parameter.json │ │ ├── one_partial_coverage.json │ │ ├── test_empty_operation.json │ │ └── two_coverage.json │ └── v3/ │ ├── operations/ │ │ └── pet_petId.yaml │ ├── petstory.yaml │ ├── petstory_no_tags.yaml │ ├── petstory_operation_wo_tags.yaml │ ├── petstory_ref_operations.yaml │ ├── petstory_with_x_example.yaml │ ├── petstory_without_parameters.yaml │ └── swagger-coverage-output/ │ ├── empty_parameters.yaml │ ├── enum_param_1.yaml │ ├── enum_param_2.yaml │ ├── ignore_header.yaml │ ├── match_pattern.yaml │ ├── missed_in_swagger.yaml │ ├── missed_in_swagger_without_x_example.yaml │ ├── not_200.yaml │ ├── one_coverage.yaml │ ├── one_parameter.yaml │ ├── one_partial_coverage.yaml │ ├── test_empty_operation.yaml │ └── two_coverage.yaml ├── swagger-coverage-commons/ │ ├── build.gradle.kts │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── github/ │ │ └── viclovsky/ │ │ └── swagger/ │ │ └── coverage/ │ │ ├── CoverageOutputReader.java │ │ ├── CoverageOutputWriter.java │ │ ├── FileSystemOutputReader.java │ │ ├── FileSystemOutputWriter.java │ │ ├── SwaggerCoverageConstants.java │ │ ├── SwaggerCoverageReadException.java │ │ ├── SwaggerCoverageUtils.java │ │ ├── SwaggerCoverageWriteException.java │ │ ├── model/ │ │ │ └── SwaggerCoverage2ModelJackson.java │ │ └── utils/ │ │ └── FreemarkerUtils.java │ └── resources/ │ ├── details/ │ │ ├── condition.ftl │ │ ├── operation.ftl │ │ └── tag.ftl │ ├── message.en │ ├── message.ru │ ├── report.ftl │ ├── sections/ │ │ ├── generation.ftl │ │ └── summary.ftl │ └── ui.ftl ├── swagger-coverage-karate/ │ ├── README.md │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── github/ │ │ │ └── viclovsky/ │ │ │ └── swagger/ │ │ │ └── coverage/ │ │ │ └── karate/ │ │ │ ├── Request.java │ │ │ ├── RequestWriter.java │ │ │ ├── SwaggerCoverageOptions.java │ │ │ └── SwaggerCoverageRunner.java │ │ └── resources/ │ │ ├── httpProxy.feature │ │ └── karate-base.js │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── github/ │ │ └── viclovsky/ │ │ └── swagger/ │ │ └── coverage/ │ │ └── karate/ │ │ ├── RequestWriterTest.java │ │ └── SwaggerCoverageRunnerTest.java │ └── resources/ │ ├── api-test-coverage-v2/ │ │ ├── swagger-coverage-config.json │ │ └── swagger-specification.json │ ├── api-test-coverage-v3/ │ │ ├── swagger-coverage-config.json │ │ └── swagger-specification.yaml │ ├── petv2.feature │ ├── petv3.feature │ ├── request.json │ └── wiremock/ │ └── __files/ │ ├── openapi.yaml │ └── swagger.json └── swagger-coverage-rest-assured/ ├── build.gradle.kts └── src/ ├── main/ │ └── java/ │ └── com/ │ └── github/ │ └── viclovsky/ │ └── swagger/ │ └── coverage/ │ ├── SwaggerCoverageRestAssured.java │ └── SwaggerCoverageV3RestAssured.java └── test/ └── java/ └── com/ └── github/ └── viclovsky/ └── swagger/ └── coverage/ ├── RequestLoggerFilterTest.java └── RequestLoggerV3FilterTest.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CODEOWNERS ================================================ * @viclovsky ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ [//]: # ( . This repository's issues are reserved for feature requests and bug reports. . . Make sure you have a clear name for your issue. The name should start with a capital . letter and no dot is required in the end of the sentence. An example of good issue names: . . - Bad perfomance with a large specification . - Support OAS3 ) #### I'm submitting a ... - [ ] bug report - [ ] feature request #### What is the current behavior? #### If the current behavior is a bug, please provide steps to reproduce, broken swagger specification and swagger-coverage-output: #### What is the expected behavior? #### What is the motivation / use case for changing the behavior? #### Other information [//]: # ( . e.g. detailed explanation, stacktraces, related issues, suggestions . how to fix, links for us to have more context ) ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: gradle directory: / schedule: interval: weekly - package-ecosystem: github-actions directory: / schedule: interval: weekly ================================================ FILE: .github/release-drafter.yml ================================================ name-template: '$NEXT_MINOR_VERSION' tag-template: '$NEXT_MINOR_VERSION' categories: - title: '🚀 New Features' label: 'type:new feature' - title: '🔬 Improvements' label: 'type:improvement' - title: '🐞 Bug Fixes' label: 'type:bug' change-template: '* $TITLE (via #$NUMBER) - @$AUTHOR' template: | $CHANGES ## 👀 Links [Commits since $PREVIOUS_TAG](https://github.com/viclovsky/swagger-coverage/compare/$PREVIOUS_TAG...master) ================================================ FILE: .github/workflows/build.yml ================================================ name: CI on: pull_request: branches: - '*' push: branches: - 'master' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: 8 cache: 'gradle' - name: Gradle Build run: ./gradlew build - name: Run Demo if: github.event_name == 'pull_request' run: ./run.sh - uses: actions/upload-artifact@v3 if: github.event_name == 'pull_request' with: name: custom-report.html path: custom-report.html ================================================ FILE: .github/workflows/gradle-wrapper-validation.yml ================================================ name: "Validate Gradle Wrapper" on: push: branches: - master paths: - 'gradlew' - 'gradlew.bat' - 'gradle/wrapper/**' - '.github/workflows/gradle-wrapper-validation.yml' pull_request: branches: - master paths: - 'gradlew' - 'gradlew.bat' - 'gradle/wrapper/**' - '.github/workflows/gradle-wrapper-validation.yml' jobs: validation: name: "Validation" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 ================================================ FILE: .github/workflows/label-verify.yml ================================================ name: "Verify type labels" on: pull_request: types: [opened, labeled, unlabeled, synchronize] jobs: triage: runs-on: ubuntu-latest steps: - uses: zwaldowski/match-label-action@v2 with: allowed: 'type:new feature, type:improvement, type:bug' ================================================ FILE: .github/workflows/release-draft.yml ================================================ name: Release Draft on: push: branches: - master jobs: update_draft_release: runs-on: ubuntu-latest steps: - uses: toolmantim/release-drafter@v5.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: release: types: [published] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Set up JDK uses: actions/setup-java@v1 with: java-version: 8 - run: echo "::set-output name=version::${GITHUB_REF:10}" id: release - name: "Gradle Build" run: ./gradlew build -Pversion=${{ steps.release.outputs.version }} - name: Prepare to publish run: | echo '${{secrets.GPG_KEY_CONTENTS}}' | base64 -d > ${{ github.workspace }}/publish_key.gpg gpg --quiet --batch --yes --decrypt --passphrase="${{secrets.SECRET_PASSPHRASE}}" \ --output ${{ github.workspace }}/secret.gpg ${{ github.workspace }}/publish_key.gpg - name: "Publish packages to Maven Central" env: SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} run: ./gradlew --no-parallel publishAllPublicationsToSonatypeRepository -Pversion=${{ steps.release.outputs.version }} -Psigning.secretKeyRingFile=${{ github.workspace }}/secret.gpg -Psigning.keyId=${{secrets.SIGNING_KEYID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} - name: "Publish Zip to GitHub" uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ github.event.release.upload_url }} asset_path: ./swagger-coverage-commandline/build/distributions/swagger-coverage-commandline-${{ steps.release.outputs.version }}.zip asset_name: swagger-coverage-${{ steps.release.outputs.version }}.zip asset_content_type: application/octet-stream - name: "Publish Tar to GitHub" uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ github.event.release.upload_url }} asset_path: ./swagger-coverage-commandline/build/distributions/swagger-coverage-commandline-${{ steps.release.outputs.version }}.tar asset_name: swagger-coverage-${{ steps.release.outputs.version }}.tar asset_content_type: application/octet-stream ================================================ FILE: .gitignore ================================================ # IDEA files .idea *.iml # Gradle .gradle build # Mac OS .DS_Store # Allure allure-results bin/ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [2019] [viclovsky] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [license]: http://www.apache.org/licenses/LICENSE-2.0 "Apache License 2.0" [release]: https://github.com/viclovsky/swagger-coverage/releases/latest "Latest release" [release-badge]: https://img.shields.io/github/release/viclovsky/swagger-coverage.svg?style=flat [maven]: https://repo.maven.apache.org/maven2/com/github/viclovsky/swagger-coverage-commandline/ "Maven Central" [maven-badge]: https://img.shields.io/maven-central/v/com.github.viclovsky/swagger-coverage-commandline.svg?style=flat [![Build Status](https://github.com/viclovsky/swagger-coverage/workflows/Build/badge.svg)](https://github.com/viclovsky/swagger-coverage/actions) [![release-badge][]][release] [![maven-badge][]][maven] # swagger-coverage Swagger-coverage gives a full picture about coverage of API tests (regression) based on OAS (Swagger). By saying coverage we mean not a broad theme functionality, but presence (or absence) of calls defined by API methods, parameters, return codes or other conditions which corresponds specification of API. ![Swagger Coverage Report](.github/swagger-coverage.png) ## How it works Producing coverage report consists of two parts. Firstly, during test execution, filter/interceptor/proxy save information of calls in swagger format in specific folder on executing tests. The next stage is to compare saved result with generated conditions from current API specification and builds report. ## How to use and examples You can use swagger-coverage with any language and framework. You need to have proxy/filter/interceptor that accumulates data in swagger format. Swagger-coverage have rest-assured integration from the box. > There is also a Karate integration, which has its own [manual](/swagger-coverage-karate/README.md). Add filter dependency: ```xml com.github.viclovsky swagger-coverage-rest-assured ${latest-swagger-coverage-version} ``` or if use gradle, it can be added like ``` compile "com.github.viclovsky:swagger-coverage-rest-assured:$latest-swagger-coverage-version" ``` Just add filter into test client SwaggerCoverageRestAssured (SwaggerCoverageV3RestAssured for v3). For instance, as presented below: ```java RestAssured.given().filter(new SwaggerCoverageRestAssured()) ``` * Download and run command line. Download zip archive and unpack it. Don't forget to replace {latest-swagger-coverage-version} to latest version. ``` wget https://github.com/viclovsky/swagger-coverage/releases/download/{latest-swagger-coverage-version}/swagger-coverage-{latest-swagger-coverage-version}.zip unzip swagger-coverage-commandline-{latest-swagger-coverage-version}.zip ``` Here is help of unzip swagger-commandline ``` ./swagger-coverage-commandline --help Options: * -s, --spec Path to local or URL to remote swagger specification. * -i, --input Path to folder with generated files with coverage. -c, --configuration Path to file with report configuration. --help Print commandline help. -q, --quiet Switch on the quiet mode. Default: false -v, --verbose Switch on the verbose mode. Default: false ``` To compare result of API tests with current API specification and build report call command line tool after running tests like that: ``` ./swagger-coverage-commandline -s swagger.json -i swagger-coverage-output ``` Output of the command: ``` 19:21:21 INFO OperationSwaggerCoverageCalculator - Empty coverage: ... 19:21:21 INFO OperationSwaggerCoverageCalculator - Partial coverage: ... 19:21:21 INFO OperationSwaggerCoverageCalculator - Full coverage: ... 19:21:21 INFO OperationSwaggerCoverageCalculator - Conditions: 874/2520 19:21:21 INFO OperationSwaggerCoverageCalculator - Empty coverage 49.284 % 19:21:21 INFO OperationSwaggerCoverageCalculator - Partial coverage 12.034 % 19:21:21 INFO OperationSwaggerCoverageCalculator - Full coverage 38.682 % 19:21:21 INFO FileSystemResultsWriter - Write html report in file '.../swagger-coverage-report.html' ``` Results (swagger-coverage-report.html/swagger-coverage-results.json) will be created after running of swagger-coverage. ## Configuration options Swagger-coverage report can be configured by json-file. You can control list of coverage, which be generated and checked for results. ## Rules configuration options Options for different rules are placed in "rules" section. You can disable some rules or change their behavior. #### Checking response http-status This rule create condition for every status from *responses*-section of swagger specification. Condition mark *covered* when report generator find specific status in results files. Options for this rules are placed in *status* subsection in *rules* sections. You can setup next options: **enable** - *true/false*. You can disable this rule. Default value is *true*. **filter** - *[val1,val2]*. Rule will ignore all status, which not in filter list. **ignore** - *[val1,val2]*. Rule will ignore all status, which in ignore list. ```` { "rules" : { "status": { "enable": true, "ignore": ["400","500"], "filter": ["200"] }, .... }, .... } ```` #### Checking the list of declared and received statuses This rule create condition for comparing declared and received status. Condition marked as *covered* when result not contains any of undeclared status. *Uncovered* state of this condition indicates missed status in original swagger-documentation or server errors. Options for this rules are placed in *only-declared-status* subsection in *rules* sections. You can setup next options: **enable** - *true/false*. You can disable this rule. Default value is *true*. ```` { "rules" : { .... "only-declared-status" : { "enable" : true } }, .... } ```` #### Excluding deprecated operations from the coverage report statistic This rule is created for cases when you don't want to measure coverage of deprecated operations, but only for actual ones.
If an operation is deprecated then it will be excluded from *Full*, *Partial*, and *Empty* categories and won't affect the "Operations coverage summary" Options for this rule are placed in "*exclude-deprecated*" subsection in *rules* sections. You can set up next options: **enable** - *true/false*.
By default, this rule is not enabled. Add it to the config file with *true* value to enable this rule, like in the example below: ```` { "rules" : { .... "exclude-deprecated" : { "enable" : true } }, .... } ```` If you need you can add your rules for generation of conditions. So, please, send your PRs. ## Result writer configuration Options for report generation setting are placed in *writers* sections. #### HTML report writer Options for html-report placed in subsection *html* of *writers* sections. You can setup next options: **locale** - two latter language code. Now supported only *en/ru*. **filename** - filename for html report. **numberFormat** - [Extended Java decimal format](https://freemarker.apache.org/docs/ref_builtins_number.html#topic.extendedJavaDecimalFormat) to control how numbers are displayed in the report. ```` { .... "writers": { "html": { "locale": "ru", "filename":"report.html", "numberFormat": "0.##" } } } ```` #### Report customization To customize your http report with your own template set full path to the template like below: ```` { .... "writers": { "html": { .... "customTemplatePath": "/full/path/to/report_custom.ftl" } } } ```` [Look here](https://github.com/swagger-api/swagger-parser/blob/master/modules/swagger-parser-core/src/main/java/io/swagger/v3/parser/core/models/ParseOptions.java) to see all available options. ## Demo I have prepared several tests. Thus you are able to have a look and touch swagger-coverage. Just run ```run.sh``` script. ## Important remark Swagger-coverage works fine with clients which were generated from swagger (for example: https://github.com/OpenAPITools/openapi-generator). Because all methods/parameters which will be saved are 100% compatible with current API specification. ## Requirements For a moment swagger-coverage is compatible only with OpenApi specifications v2 & v3. It is possible that swagger-coverage will support other versions. ## Pull Requests My project is open for any enhancement. So, your help is much appreciated. Please, feel free to open your pull request or issue and I will consider it in several days. ## Created & Maintained By [Victor Orlovsky](https://github.com/viclovsky) ## Contributing to swagger-coverage Thanks to all people who contributed. Especially * [@TemaMak](https://github.com/TemaMak) * [@Emilio-Pega](https://github.com/Emilio-Pega) who have contributed significant improvements to swagger-coverage. ## License Swagger coverage is released under version 2.0 of the [Apache License](http://www.apache.org/licenses/LICENSE-2.0) ================================================ FILE: build.gradle.kts ================================================ import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension buildscript { repositories { mavenLocal() } } plugins { java id("io.spring.dependency-management") version "1.0.11.RELEASE" } group = "com.github.viclovsky" version = version val root = rootProject.projectDir val gradleScriptDir by extra("$root/gradle") java { sourceCompatibility = JavaVersion.VERSION_1_8 } configure(subprojects) { group = "com.github.viclovsky" version = version apply(plugin = "io.spring.dependency-management") apply(plugin = "java") apply(plugin = "java-library") configure { imports { mavenBom("com.fasterxml.jackson:jackson-bom:2.9.8") } dependencies { dependency("org.freemarker:freemarker:2.3.31") //swagger 2.x dependency("io.swagger:swagger-models:1.6.6") //swagger 3.x dependency("io.swagger.core.v3:swagger-core:2.2.0") dependency("io.swagger.core.v3:swagger-models:2.2.0") dependency("io.swagger.parser.v3:swagger-parser:2.0.32") dependency("org.slf4j:slf4j-api:1.7.32") dependency("ch.qos.logback:logback-classic:1.2.10") dependency("com.beust:jcommander:1.81") dependency("junit:junit:4.13.2") dependency("org.hamcrest:hamcrest:2.2") dependency("com.jayway.jsonpath:json-path-assert:2.7.0") dependency("io.rest-assured:rest-assured:4.4.0") dependency("com.github.tomakehurst:wiremock:2.27.2") dependency("com.fasterxml.jackson.core:jackson-core:2.12.3") dependency("com.fasterxml.jackson.core:jackson-databind:2.12.3") dependency("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.3") dependency("com.fasterxml.jackson.core:jackson-annotations:2.12.3") dependency("org.springframework:spring-web:5.3.7") dependency("com.intuit.karate:karate-core:1.2.0.RC1") } } java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } val sourceJar by tasks.creating(Jar::class) { from(sourceSets.getByName("main").allSource) archiveClassifier.set("sources") } val javadocJar by tasks.creating(Jar::class) { from(tasks.getByName("javadoc")) archiveClassifier.set("javadoc") } tasks.withType(Javadoc::class) { (options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet") } tasks.withType { enabled = false } artifacts.add("archives", sourceJar) artifacts.add("archives", javadocJar) tasks.compileJava { options.encoding = "UTF-8" } tasks.compileTestJava { options.encoding = "UTF-8" options.compilerArgs.add("-parameters") } tasks.jar { manifest { attributes(mapOf( "Implementation-Title" to project.name, "Implementation-Version" to project.version )) } } apply(from = "$gradleScriptDir/maven-publish.gradle") } repositories { mavenCentral() } ================================================ FILE: gradle/maven-publish.gradle ================================================ apply plugin: 'maven-publish' apply plugin: 'signing' publishing { publications { gpr(MavenPublication) { from components.java artifact sourceJar artifact javadocJar pom { name = project.name description = project.name url = "https://github.com/viclovsky/swagger-coverage" organization { name = "viclovsky" url = "https://github.com/viclovsky" } licenses { license { name = 'The Apache Software License, Version 2.0' url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' distribution = 'repo' } } scm { url = 'https://github.com/viclovsky/swagger-coverage' connection = 'scm:git:git://github.com/viclovsky/swagger-coverage' developerConnection = 'scm:git:git://github.com/viclovsky/swagger-coverage' } developers { developer { id = 'viclovsky' name = 'Victor Orlovsky' email = 'viclovsky@gmail.com' } } issueManagement { system = 'Github Issues' url = 'https://github.com/viclovsky/swagger-coverage/issues' } } } } repositories { maven { name = "sonatype" url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2") credentials { username = project.hasProperty('SONATYPE_USERNAME') ? project.property('SONATYPE_USERNAME') : System.getenv('SONATYPE_USERNAME') password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') } } } } signing { sign publishing.publications } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ version=1.0-SNAPSHOT ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: jitpack.yml ================================================ jdk: - openjdk8 install: - ./gradlew clean build publishToMavenLocal -x signGprPublication ================================================ FILE: run.sh ================================================ ./gradlew clean build -x test rm -r -f swagger-coverage-commandline-1.0-SNAPSHOT unzip swagger-coverage-commandline/build/distributions/swagger-coverage-commandline-1.0-SNAPSHOT.zip ./swagger-coverage-commandline-1.0-SNAPSHOT/bin/swagger-coverage-commandline -s swagger-coverage-commandline/src/test/resources/v3/petstory.yaml -i swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output -c swagger-coverage-commandline/src/test/resources/configuration.json ================================================ FILE: settings.gradle.kts ================================================ rootProject.name = "swagger-coverage" include("swagger-coverage-commandline") include("swagger-coverage-rest-assured") include("swagger-coverage-commons") include("swagger-coverage-karate") ================================================ FILE: swagger-coverage-commandline/build.gradle.kts ================================================ plugins { java `java-library` application } description = "Swagger-coverage Commandline" application { mainClass.set("com.github.viclovsky.swagger.coverage.CommandLine") } java { sourceCompatibility = JavaVersion.VERSION_1_8 } repositories { mavenCentral() } dependencies { api(project(":swagger-coverage-commons")) implementation("io.swagger.parser.v3:swagger-parser") implementation("org.slf4j:slf4j-api") implementation("ch.qos.logback:logback-classic") implementation("com.beust:jcommander") implementation("org.springframework:spring-web") testImplementation("junit:junit") testImplementation("org.hamcrest:hamcrest") testImplementation("com.jayway.jsonpath:json-path-assert") } tasks { test { //set the workingDir to the build dir so we don't pollute the main project dir //with generated test files workingDir(buildDir) } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/CommandLine.java ================================================ package com.github.viclovsky.swagger.coverage; import ch.qos.logback.classic.Level; import com.beust.jcommander.JCommander; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; import com.github.viclovsky.swagger.coverage.core.generator.Generator; import com.github.viclovsky.swagger.coverage.option.MainOptions; import com.github.viclovsky.swagger.coverage.option.VerboseOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Optional; @Parameters(commandNames = "swagger-coverage", commandDescription = "Swagger-coverage Commandline") public class CommandLine { @ParametersDelegate private MainOptions mainOptions = new MainOptions(); @ParametersDelegate private VerboseOptions verboseOptions = new VerboseOptions(); private static final Logger LOGGER = LoggerFactory.getLogger(CommandLine.class); private final JCommander commander = new JCommander(this); public static void main(final String[] argv) { final CommandLine commandLine = new CommandLine(); final ExitCode exitCode = commandLine .parse(argv) .orElseGet(commandLine::run); System.exit(exitCode.getCode()); } @SuppressWarnings({"PMD.AvoidLiteralsInIfCondition", "ReturnCount"}) public Optional parse(final String... args) { if (args.length == 0) { printUsage(commander); return Optional.of(ExitCode.ARGUMENT_PARSING_ERROR); } try { commander.parse(args); } catch (ParameterException e) { LOGGER.debug("Error during arguments parsing: {}", e); LOGGER.info("Could not parse arguments: {}", e.getMessage()); printUsage(commander); return Optional.of(ExitCode.ARGUMENT_PARSING_ERROR); } return Optional.empty(); } private ExitCode run() { ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); if (verboseOptions.isQuiet()) { rootLogger.setLevel(Level.OFF); } if (verboseOptions.isVerbose()) { rootLogger.setLevel(Level.DEBUG); } if (mainOptions.isHelp()) { printUsage(commander); return ExitCode.NO_ERROR; } new Generator().setInputPath(mainOptions.getInputPath()) .setSpecPath(mainOptions.getSpecPath()) .setConfigurationPath(mainOptions.getConfiguration()) .run(); return ExitCode.NO_ERROR; } private void printUsage(final JCommander commander) { commander.usage(); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/ExitCode.java ================================================ package com.github.viclovsky.swagger.coverage; public enum ExitCode { NO_ERROR(0), GENERIC_ERROR(1), ARGUMENT_PARSING_ERROR(127); private final int code; ExitCode(final int code) { this.code = code; } public int getCode() { return code; } public boolean isSuccess() { return 0 == code; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/configuration/Configuration.java ================================================ package com.github.viclovsky.swagger.coverage.configuration; import com.github.viclovsky.swagger.coverage.configuration.options.ConfigurationOptions; import com.github.viclovsky.swagger.coverage.configuration.options.RuleConfigurationOptions; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import com.github.viclovsky.swagger.coverage.core.writer.CoverageResultsWriter; import io.swagger.v3.oas.models.OpenAPI; import java.util.List; import java.util.stream.Collectors; public class Configuration { protected ConfigurationOptions options = new ConfigurationOptions(); private List defaultRules; private List registeredBuilders; /** * Configured data **/ private List configuredRules = null; private List configuredBuilders = null; private List configuredResultsWriters = null; public Configuration() { } public List getRulesList() { if (configuredRules == null) { configuredRules = defaultRules.stream() .filter(rule -> enableByRuleOptions(rule.getId())) .map(rule -> rule.configure(options.getRules().getOrDefault(rule.getId(), new RuleConfigurationOptions()))) .collect(Collectors.toList()); } return configuredRules; } public List getStatisticsBuilders(OpenAPI specification) { if (configuredBuilders == null) { configuredBuilders = registeredBuilders .stream().map(builder -> builder .configure(options) .configure(specification, getRulesList()) ) .collect(Collectors.toList()); } return configuredBuilders; } protected boolean enableByRuleOptions(String id) { RuleConfigurationOptions option = options.getRules().get(id); if (option != null) { return option.isEnable(); } return true; } public Configuration setOptions(ConfigurationOptions options) { this.options = options; return this; } public RuleConfigurationOptions getOption(String optionKey) { return this.options.getRules().get(optionKey); } public Configuration setDefaultRules(List defaultRules) { this.defaultRules = defaultRules; return this; } public Configuration setRegisteredBuilders(List registeredBuilders) { this.registeredBuilders = registeredBuilders; return this; } public List getConfiguredBuilders() { return configuredBuilders; } public Configuration setConfiguredBuilders(List configuredBuilders) { this.configuredBuilders = configuredBuilders; return this; } public List getConfiguredResultsWriters() { return configuredResultsWriters; } public Configuration setConfiguredResultsWriters(List configuredResultsWriters) { this.configuredResultsWriters = configuredResultsWriters; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/configuration/ConfigurationBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.configuration; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.viclovsky.swagger.coverage.configuration.options.ConfigurationOptions; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder.ConditionStatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder.ConfigurationStatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder.FlatOperationBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder.SwaggerInfoBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder.TagStatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder.ZeroCallStatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.prebuilder.CoverageStatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.prebuilder.GenerationStatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.rule.body.PropertyEnumAllValuesRule; import com.github.viclovsky.swagger.coverage.core.rule.body.PropertyNotEmptyRule; import com.github.viclovsky.swagger.coverage.core.rule.body.PropertyNotOnlyEnumValuesRule; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import com.github.viclovsky.swagger.coverage.core.rule.parameter.EmptyHeaderRule; import com.github.viclovsky.swagger.coverage.core.rule.body.NotEmptyBodyRule; import com.github.viclovsky.swagger.coverage.core.rule.parameter.EnumAllValuesRule; import com.github.viclovsky.swagger.coverage.core.rule.parameter.NotEmptyParameterRule; import com.github.viclovsky.swagger.coverage.core.rule.parameter.NotOnlyEnumValuesRule; import com.github.viclovsky.swagger.coverage.core.rule.status.HTTPStatusRule; import com.github.viclovsky.swagger.coverage.core.rule.status.OnlyDeclaredHTTPStatusesRule; import com.github.viclovsky.swagger.coverage.core.writer.CoverageResultsWriter; import com.github.viclovsky.swagger.coverage.core.writer.FileSystemResultsWriter; import com.github.viclovsky.swagger.coverage.core.writer.HtmlReportResultsWriter; import com.github.viclovsky.swagger.coverage.core.writer.LogResultsWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class ConfigurationBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationBuilder.class); public static Configuration build(Path path) { Configuration configuration = new Configuration(); ConfigurationOptions options = new ConfigurationOptions(); ObjectMapper mapper = new ObjectMapper(); try { if (path != null) { options = mapper.readValue(path.toFile(), ConfigurationOptions.class); } } catch (IOException e) { LOGGER.info("can't read configuration, use default configuration"); } configuration.setOptions(options) .setDefaultRules(getDefaultList()) .setRegisteredBuilders(getDefaultBuilderList()) .setConfiguredResultsWriters(getResultsWriters(options)); return configuration; } private static List getResultsWriters(ConfigurationOptions options) { List configuredResultsWriters = new ArrayList<>(); if (options.getWriters().isEmpty()) { configuredResultsWriters.add(new HtmlReportResultsWriter()); configuredResultsWriters.add(new LogResultsWriter()); configuredResultsWriters.add(new FileSystemResultsWriter()); } else { options.getWriters() .forEach((key, value) -> { switch (key) { case "html": configuredResultsWriters.add( new HtmlReportResultsWriter(value) ); break; case "LOGGER": configuredResultsWriters.add( new LogResultsWriter() ); break; case "json": configuredResultsWriters.add( new FileSystemResultsWriter(value.getFilename()) ); break; } }); } return configuredResultsWriters; } private static List getDefaultList() { List registeredRules = new ArrayList<>(); registeredRules.add(new HTTPStatusRule()); registeredRules.add(new NotEmptyParameterRule()); registeredRules.add(new EnumAllValuesRule()); registeredRules.add(new NotEmptyBodyRule()); registeredRules.add(new OnlyDeclaredHTTPStatusesRule()); registeredRules.add(new EmptyHeaderRule()); registeredRules.add(new NotOnlyEnumValuesRule()); registeredRules.add(new PropertyEnumAllValuesRule()); registeredRules.add(new PropertyNotOnlyEnumValuesRule()); registeredRules.add(new PropertyNotEmptyRule()); return registeredRules; } private static List getDefaultBuilderList() { List registeredBuilders = new ArrayList<>(); registeredBuilders.add(new CoverageStatisticsBuilder()); registeredBuilders.add(new GenerationStatisticsBuilder()); registeredBuilders.add(new ConditionStatisticsBuilder()); registeredBuilders.add(new ZeroCallStatisticsBuilder()); registeredBuilders.add(new TagStatisticsBuilder()); registeredBuilders.add(new ConfigurationStatisticsBuilder()); registeredBuilders.add(new FlatOperationBuilder()); registeredBuilders.add(new SwaggerInfoBuilder()); return registeredBuilders; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/configuration/options/ConfigurationOptions.java ================================================ package com.github.viclovsky.swagger.coverage.configuration.options; import io.swagger.v3.parser.core.models.ParseOptions; import java.util.HashMap; import java.util.Map; public class ConfigurationOptions { private Map rules = new HashMap<>(); private Map writers = new HashMap<>(); public Map getRules() { return rules; } public ConfigurationOptions setRules(Map rules) { this.rules = rules; return this; } public Map getWriters() { return writers; } public ConfigurationOptions setWriters(Map writers) { this.writers = writers; return this; } @Override public String toString() { return "ConfigurationOptions{" + "rules=" + rules.toString() + ", writers=" + writers.toString() + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/configuration/options/MainOptions.java ================================================ package com.github.viclovsky.swagger.coverage.configuration.options; public class MainOptions { //todo: add more options @Override public String toString() { return "MainOptions{" + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/configuration/options/ResultsWriterOptions.java ================================================ package com.github.viclovsky.swagger.coverage.configuration.options; public class ResultsWriterOptions { private String filename; private String locale = "en"; private String customTemplatePath; private String numberFormat = "0.###"; public String getFilename() { return filename; } public ResultsWriterOptions setFilename(String filename) { this.filename = filename; return this; } public String getLocale() { return locale; } public ResultsWriterOptions setLocale(String locale) { this.locale = locale; return this; } @Override public String toString() { return "ResultsWriterOptions{" + ", filename='" + filename + '\'' + ", locale='" + locale + '\'' + ", customTemplatePath='" + customTemplatePath + '\'' + ", numberFormat='" + numberFormat + '\'' + '}'; } public ResultsWriterOptions setCustomTemplatePath(String customTemplatePath) { this.customTemplatePath = customTemplatePath; return this; } public String getCustomTemplatePath() { return customTemplatePath; } public String getNumberFormat() {return numberFormat;} public ResultsWriterOptions setNumberFormat(String numberFormat){ this.numberFormat = numberFormat; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/configuration/options/RuleConfigurationOptions.java ================================================ package com.github.viclovsky.swagger.coverage.configuration.options; import java.util.List; public class RuleConfigurationOptions { private boolean enable = true; private List filter; private List ignore; public boolean isEnable() { return enable; } public RuleConfigurationOptions setEnable(boolean enable) { this.enable = enable; return this; } public List getFilter() { return filter; } public RuleConfigurationOptions setFilter(List filter) { this.filter = filter; return this; } public List getIgnore() { return ignore; } public RuleConfigurationOptions setIgnore(List ignore) { this.ignore = ignore; return this; } @Override public String toString() { return "RuleConfigurationOptions{" + "enable=" + enable + ", filter=" + filter + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/generator/Generator.java ================================================ package com.github.viclovsky.swagger.coverage.core.generator; import com.github.viclovsky.swagger.coverage.CoverageOutputReader; import com.github.viclovsky.swagger.coverage.FileSystemOutputReader; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.configuration.ConfigurationBuilder; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsBuilder; import io.swagger.parser.OpenAPIParser; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.parser.core.models.AuthorizationValue; import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class Generator { private static final Logger LOGGER = LoggerFactory.getLogger(Generator.class); private URI specPath; private List specAuths; private Path inputPath; private Path configurationPath; private final OpenAPIParser parser = new OpenAPIParser(); private List statisticsBuilders = new ArrayList<>(); public void run() { Configuration configuration = ConfigurationBuilder.build(configurationPath); ParseOptions parseOptions = new ParseOptions(); parseOptions.setResolve(true); SwaggerParseResult parsed = parser.readLocation(specPath.toString(), specAuths, parseOptions); parsed.getMessages().forEach(LOGGER::info); OpenAPI spec = parsed.getOpenAPI(); LOGGER.info("spec is {}", spec); statisticsBuilders = configuration.getStatisticsBuilders(spec); CoverageOutputReader reader = new FileSystemOutputReader(getInputPath()); reader.getOutputs().forEach(this::processFile); Results result = new Results(); statisticsBuilders.stream().filter(StatisticsBuilder::isPreBuilder).forEach( statisticsBuilder -> statisticsBuilder.build(result, configuration)); statisticsBuilders.stream().filter(StatisticsBuilder::isPostBuilder).forEach( statisticsBuilder -> statisticsBuilder.build(result, configuration)); configuration.getConfiguredResultsWriters().forEach(writer -> writer.write(result)); } public void processFile(Path path) { SwaggerParseResult parsed = parser.readLocation(path.toUri().toString(), null, null); parsed.getMessages().forEach(LOGGER::info); OpenAPI spec = parsed.getOpenAPI(); statisticsBuilders.stream().filter(StatisticsBuilder::isPreBuilder).forEach(builder -> builder.add(path.toString()).add(spec)); } public URI getSpecPath() { return specPath; } public Generator setSpecPath(URI specPath) { this.specPath = specPath; return this; } public List getSpecAuths() { return specAuths; } public Generator setSpecAuths(List specAuths) { this.specAuths = specAuths; return this; } public Path getInputPath() { return inputPath; } public Generator setInputPath(Path inputPath) { this.inputPath = inputPath; return this; } public Path getConfigurationPath() { return configurationPath; } public Generator setConfigurationPath(Path configurationPath) { this.configurationPath = configurationPath; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/generator/OperationConditionGenerator.java ================================================ package com.github.viclovsky.swagger.coverage.core.generator; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.ConditionOperationCoverage; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.model.OperationsHolder; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; public class OperationConditionGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(OperationConditionGenerator.class); public static Map getOperationMap(OpenAPI swagger, List rules) { OperationsHolder operations = SwaggerSpecificationProcessor.extractOperation(swagger); Map coverage = new TreeMap<>(); operations.getOperations().forEach((key, value) -> { ConditionOperationCoverage oc = buildConditionOperationCoverage(value, rules); LOGGER.debug(String.format("put operation %s", key)); coverage.put(key, oc); }); return coverage; } private static ConditionOperationCoverage buildConditionOperationCoverage(Operation operation, List rules) { ConditionOperationCoverage operationCoverage = new ConditionOperationCoverage(); operationCoverage.setOperation(operation); operationCoverage.setConditions(generateConditionList(operation, rules)); return operationCoverage; } private static List generateConditionList(Operation operation, List rules) { List conditions = rules .stream() .map(rule -> rule.createCondition(operation)) .filter(Objects::nonNull) .flatMap(List::stream) .collect(Collectors.toList()); LOGGER.debug(String.format("created list is %s", conditions)); return conditions; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/generator/SwaggerSpecificationProcessor.java ================================================ package com.github.viclovsky.swagger.coverage.core.generator; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.model.OperationsHolder; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class SwaggerSpecificationProcessor { private static final String X_EXAMPLE = "x-example"; public static OperationsHolder extractOperation(OpenAPI swagger) { OperationsHolder operations = new OperationsHolder(); swagger.getPaths().keySet().forEach(path -> swagger.getPaths().get(path).readOperationsMap().forEach((httpMethod, operation) -> operations.addOperation(new OperationKey().setPath(path).setHttpMethod(httpMethod), operation) )); return operations; } public static String extractValue(Parameter p) { if (p.getExtensions() != null && p.getExtensions().containsKey(X_EXAMPLE)) { return (String) p.getExtensions().get(X_EXAMPLE); } if(p.getExample() != null) { return p.getExample().toString(); } return p.getName(); } public static String extractValue(Schema schema) { if (schema.getExtensions() !=null && schema.getExtensions().containsKey(X_EXAMPLE)) { return (String) schema.getExtensions().get(X_EXAMPLE); } if(schema.getExample() != null) { return schema.getExample().toString(); } return schema.getName(); } public static List extractEnum(Parameter p) { return extractEnum(p.getSchema()); } public static List extractEnum(Schema schema) { List enums = null; if (schema != null) { enums = schema.getEnum(); if (enums == null && schema instanceof ArraySchema && ((ArraySchema) schema).getItems() != null) { enums = ((ArraySchema) schema).getItems().getEnum(); } } if (enums != null) { return ((Stream) enums.stream()) .map(o -> o.toString()) .collect(Collectors.toList()); } else { return null; } } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/model/Condition.java ================================================ package com.github.viclovsky.swagger.coverage.core.model; import io.swagger.v3.oas.models.Operation; public abstract class Condition { private String name; private String description; boolean covered = false; public Condition(String name, String description) { this.name = name; this.description = description; } public abstract void postCheck(); public abstract boolean isHasPostCheck(); public abstract boolean isNeedCheck(); public abstract boolean check(Operation operation); public abstract String getReason(); public abstract String getType(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isCovered() { return covered; } public void setCovered(boolean covered) { this.covered = covered; } @Override public String toString() { return "Condition{" + "name='" + name + '\'' + ", description='" + description + '\'' + ", covered=" + covered + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/model/ConditionOperationCoverage.java ================================================ package com.github.viclovsky.swagger.coverage.core.model; import io.swagger.v3.oas.models.Operation; import java.util.List; public class ConditionOperationCoverage { private long processCount = 0; private Operation operation; private List conditions; public Operation getOperation() { return operation; } public ConditionOperationCoverage setOperation(Operation operation) { this.operation = operation; return this; } public List getConditions() { return conditions; } public ConditionOperationCoverage setConditions(List conditions) { this.conditions = conditions; return this; } public long getProcessCount() { return processCount; } public ConditionOperationCoverage setProcessCount(long processCount) { this.processCount = processCount; return this; } public ConditionOperationCoverage increaseProcessCount() { this.processCount++; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/model/OperationKey.java ================================================ package com.github.viclovsky.swagger.coverage.core.model; import io.swagger.v3.oas.models.PathItem; public class OperationKey implements Comparable { private String path; private PathItem.HttpMethod httpMethod; @Override public String toString() { return path + " " + httpMethod; } public String getPath() { return path; } public OperationKey setPath(String path) { this.path = path; return this; } public PathItem.HttpMethod getHttpMethod() { return httpMethod; } public OperationKey setHttpMethod(PathItem.HttpMethod httpMethod) { this.httpMethod = httpMethod; return this; } @Override public int compareTo(Object o) { return this.toString().compareTo(o.toString()); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/model/OperationsHolder.java ================================================ package com.github.viclovsky.swagger.coverage.core.model; import io.swagger.v3.oas.models.Operation; import java.util.Map; import java.util.TreeMap; public class OperationsHolder { private Map operations = new TreeMap<>(); public Map getOperations() { return operations; } public OperationsHolder setOperations(Map operations) { this.operations = operations; return this; } public OperationsHolder addOperation(OperationKey operationKey, Operation operation) { this.operations.put(operationKey, operation); return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/model/SinglePredicateCondition.java ================================================ package com.github.viclovsky.swagger.coverage.core.model; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import io.swagger.v3.oas.models.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SinglePredicateCondition extends Condition { private static final Logger LOGGER = LoggerFactory.getLogger(SinglePredicateCondition.class); protected ConditionPredicate predicate; public SinglePredicateCondition(String name, String description) { super(name, description); } public SinglePredicateCondition(String name, String description, ConditionPredicate predicate) { super(name, description); this.predicate = predicate; } @Override public void postCheck() { this.covered = predicate.postCheck(); } @Override public boolean isHasPostCheck() { return predicate.hasPostCheck(); } @Override public boolean isNeedCheck() { return !this.covered || predicate.hasPostCheck(); } @Override public boolean check(Operation operation) { this.covered = predicate.check(operation); return this.covered; } @Override public String getReason() { if (predicate.getReason() != null) { return predicate.getReason(); } return ""; } @Override public String getType() { return predicate.getClass().getSimpleName(); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/ConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.Operation; public abstract class ConditionPredicate { public abstract boolean check(Operation operation); public abstract boolean postCheck(); public abstract boolean hasPostCheck(); public abstract String getReason(); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/DefaultBodyConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.Operation; public class DefaultBodyConditionPredicate extends ConditionPredicate { @Override public boolean check(Operation operation) { if(operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) { return operation.getRequestBody().getContent().values().size() > 0; } else { return false; } } @Override public boolean postCheck() { return false; } @Override public boolean hasPostCheck() { return false; } @Override public String getReason() { return null; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/DefaultParameterConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.List; import java.util.Map; import java.util.Optional; public class DefaultParameterConditionPredicate extends ParameterConditionPredicate { private boolean isEmpty; private String name; private String in; public DefaultParameterConditionPredicate(boolean isEmpty, String name, String in) { this.isEmpty = isEmpty; this.name = name; this.in = in; } @Override public boolean check(List params, Map responses) { if (params != null) { Optional p = params.stream().filter(ParameterUtils.equalsParam(name, in)).findFirst(); return (isEmpty() ^ p.isPresent()); } return isEmpty(); } @Override public boolean postCheck() { return false; } @Override public boolean hasPostCheck() { return false; } @Override public String getReason() { return null; } public boolean isEmpty() { return isEmpty; } public void setEmpty(boolean empty) { isEmpty = empty; } public String getName() { return name; } public void setParamName(String paramName) { this.name = name; } @Override public String toString() { return "ConditionPredicate{" + "isEmpty=" + isEmpty + ", paramName='" + name + '\'' + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/DefaultParameterValueConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; public class DefaultParameterValueConditionPredicate extends ParameterConditionPredicate { private String name; private String in; private String reason; private String expectedValue; private List currentValue = new ArrayList<>(); public DefaultParameterValueConditionPredicate(String name, String in, String value) { this.name = name; this.in = in; this.expectedValue = value; } @Override public boolean check(List params, Map responses) { if (params != null) { Optional p = params.stream().filter(ParameterUtils.equalsParam(name, in)).findFirst(); if (p.isPresent()) { String val = SwaggerSpecificationProcessor.extractValue(p.get()); currentValue.add(val); } } return currentValue.contains(expectedValue); } @Override public boolean postCheck() { return false; } @Override public boolean hasPostCheck() { return false; } @Override public String getReason() { return reason; } public String getName() { return name; } public DefaultParameterValueConditionPredicate setName(String name) { this.name = name; return this; } public String getValue() { return expectedValue; } public DefaultParameterValueConditionPredicate setValue(String value) { this.expectedValue = value; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/DefaultPropertyConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.media.Schema; import java.util.Optional; public class DefaultPropertyConditionPredicate extends PropertyConditionPredicate { protected boolean isEmpty; public DefaultPropertyConditionPredicate(String mediaTypeName, String propertyName, boolean isEmpty) { super(mediaTypeName, propertyName); this.isEmpty = isEmpty; } @Override public boolean postCheck() { return false; } @Override public boolean hasPostCheck() { return false; } @Override public String getReason() { return null; } public boolean isEmpty() { return isEmpty; } @Override protected boolean check(Optional schema) { return (isEmpty() ^ schema.isPresent()); } @Override public DefaultPropertyConditionPredicate setPropertyName(String propertyName) { return (DefaultPropertyConditionPredicate) super.setPropertyName(propertyName); } @Override public DefaultPropertyConditionPredicate setMediaTypeName(String mediaTypeName) { return (DefaultPropertyConditionPredicate) super.setMediaTypeName(mediaTypeName); } DefaultPropertyConditionPredicate setEmpty(boolean isEmpty) { this.isEmpty = isEmpty; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/DefaultStatusConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.List; import java.util.Map; public class DefaultStatusConditionPredicate extends ParameterConditionPredicate { private String statusCode; public DefaultStatusConditionPredicate(String statusCode) { this.statusCode = statusCode; } @Override public boolean check(List params, Map responses) { return responses.containsKey(getStatusCode()); } @Override public boolean postCheck() { return false; } @Override public boolean hasPostCheck() { return false; } @Override public String getReason() { return null; } public String getStatusCode() { return statusCode; } public void setStatusCode(String statusCode) { this.statusCode = statusCode; } @Override public String toString() { return "StatusConditionPredicate{" + "statusCode=" + statusCode + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/FullStatusConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class FullStatusConditionPredicate extends ParameterConditionPredicate { private Set expectedStatuses; private Set currentStatuses = new HashSet<>(); private String reason; public FullStatusConditionPredicate(Set expectedStatuses) { this.expectedStatuses = expectedStatuses; } @Override public boolean check(List params, Map responses) { responses.forEach((key, value) -> currentStatuses.add(key)); return true; } @Override public boolean postCheck() { if (currentStatuses.isEmpty() && expectedStatuses != null) { reason = "No call - no statuses..."; return false; } boolean covered = expectedStatuses.containsAll(currentStatuses); if (!covered) { currentStatuses.removeAll(expectedStatuses); reason = "Undeclared status: " + String.join(",", currentStatuses); } return covered; } @Override public boolean hasPostCheck() { return true; } @Override public String getReason() { return reason; } public Set getExpectedStatuses() { return expectedStatuses; } public FullStatusConditionPredicate setExpectedStatuses(Set expectedStatuses) { this.expectedStatuses = expectedStatuses; return this; } public Set getCurrentStatuses() { return currentStatuses; } public FullStatusConditionPredicate setCurrentStatuses(Set currentStatuses) { this.currentStatuses = currentStatuses; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/NotOnlyParameterListValueConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; public class NotOnlyParameterListValueConditionPredicate extends ParameterConditionPredicate { private String name; private String in; private String reason; private Set expectedValue = new HashSet<>(); private Set currentValue = new HashSet<>(); public NotOnlyParameterListValueConditionPredicate(String name, String in, List value) { this.name = name; this.in = in; this.expectedValue.addAll(value); reason = "Checked values: -"; } @Override public boolean check(List params, Map responses) { if (params != null) { Optional p = params.stream().filter(ParameterUtils.equalsParam(name, in)).findFirst(); if (p.isPresent()) { String val = SwaggerSpecificationProcessor.extractValue(p.get()); currentValue.add(val); } } return true; } @Override public boolean postCheck() { reason = "Checked values: " + currentValue.toString(); currentValue.removeAll(expectedValue); return !currentValue.isEmpty(); } @Override public boolean hasPostCheck() { return true; } @Override public String getReason() { return reason; } public String getName() { return name; } public NotOnlyParameterListValueConditionPredicate setName(String name) { this.name = name; return this; } public Set getValue() { return expectedValue; } public NotOnlyParameterListValueConditionPredicate setValue(Set value) { this.expectedValue = value; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/ParameterConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.List; import java.util.Map; public abstract class ParameterConditionPredicate extends ConditionPredicate { @Override public boolean check(Operation operation) { return check(operation.getParameters(), operation.getResponses()); } public abstract boolean check(List params, Map responses); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/ParameterUtils.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.function.Predicate; class ParameterUtils { private ParameterUtils() { } static Predicate equalsParam(String name, String in) { return p -> (p.getName().equals(name) && p.getIn().equals(in)); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/ParameterValueConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; public class ParameterValueConditionPredicate extends ParameterConditionPredicate { private String name; private String in; private String reason; private List expectedValue = new ArrayList<>(); private List currentValue = new ArrayList<>(); public ParameterValueConditionPredicate(String name, String in, List value) { this.name = name; this.expectedValue.addAll(value); this.in = in; } @Override public boolean check(List params, Map responses) { if (params != null) { Optional p = params.stream().filter(ParameterUtils.equalsParam(name, in)).findFirst(); if (p.isPresent()) { String val = SwaggerSpecificationProcessor.extractValue(p.get()); currentValue.add(val); } } return true; } @Override public boolean postCheck() { boolean covered = currentValue.containsAll(expectedValue); if (!covered) { expectedValue.removeAll(currentValue); reason = "Missed values " + expectedValue.toString(); } return covered; } @Override public boolean hasPostCheck() { return true; } @Override public String getReason() { return reason; } public String getName() { return name; } public ParameterValueConditionPredicate setName(String name) { this.name = name; return this; } public List getValue() { return expectedValue; } public ParameterValueConditionPredicate setValue(List value) { this.expectedValue = value; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/PropertyConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.Schema; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; public abstract class PropertyConditionPredicate extends ConditionPredicate { protected String mediaTypeName; protected String propertyName; public PropertyConditionPredicate(String mediaTypeName, String propertyName) { this.mediaTypeName = mediaTypeName; this.propertyName = propertyName; } @Override public boolean check(Operation operation) { if (operation.getRequestBody() == null || operation.getRequestBody().getContent() == null || operation.getRequestBody().getContent().isEmpty()) { return false; } Optional schema = operation.getRequestBody().getContent().entrySet() .stream() .filter(o -> mediaTypeName.equals(o.getKey())) .map(o -> o.getValue().getSchema()) .filter(Objects::nonNull) .flatMap(o -> (Stream>) o.getProperties().entrySet().stream()) .filter(o -> propertyName.equals(o.getKey())) .map(o -> o.getValue()) .findFirst(); return check(schema); } public String getPropertyName() { return propertyName; } public String getMediaTypeName() { return mediaTypeName; } public PropertyConditionPredicate setPropertyName(String propertyName) { this.propertyName = propertyName; return this; } public PropertyConditionPredicate setMediaTypeName(String mediaTypeName) { this.mediaTypeName = mediaTypeName; return this; } protected abstract boolean check(Optional schema); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/PropertyValueConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import io.swagger.v3.oas.models.media.Schema; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class PropertyValueConditionPredicate extends PropertyConditionPredicate { private List expectedValue = new ArrayList<>(); private List currentValue = new ArrayList<>(); protected String reason; public PropertyValueConditionPredicate(String mediaTypeName, String propertyName, List value) { super(mediaTypeName, propertyName); expectedValue.addAll(value); } @Override public boolean postCheck() { reason = "Checked values: " + currentValue.toString(); currentValue.removeAll(expectedValue); return !currentValue.isEmpty(); } @Override public boolean hasPostCheck() { return true; } @Override public String getReason() { return reason; } @Override protected boolean check(Optional schema) { if (schema.isPresent()) { currentValue.add(SwaggerSpecificationProcessor.extractValue(schema.get())); } return true; } public List getValue() { return expectedValue; } @Override public PropertyValueConditionPredicate setPropertyName(String propertyName) { return (PropertyValueConditionPredicate) super.setPropertyName(propertyName); } @Override public PropertyValueConditionPredicate setMediaTypeName(String mediaTypeName) { return (PropertyValueConditionPredicate) super.setMediaTypeName(mediaTypeName); } public PropertyValueConditionPredicate setValue(List value) { this.expectedValue = value; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/predicate/PropertyValueNotOnlyConditionPredicate.java ================================================ package com.github.viclovsky.swagger.coverage.core.predicate; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import io.swagger.v3.oas.models.media.Schema; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class PropertyValueNotOnlyConditionPredicate extends PropertyConditionPredicate { private List expectedValue = new ArrayList<>(); private List currentValue = new ArrayList<>(); protected String reason; public PropertyValueNotOnlyConditionPredicate(String mediaTypeName, String propertyName, List value) { super(mediaTypeName, propertyName); expectedValue.addAll(value); } @Override public boolean postCheck() { boolean covered = currentValue.containsAll(expectedValue); if (!covered) { expectedValue.removeAll(currentValue); reason = "Missed values " + expectedValue.toString(); } return covered; } @Override public boolean hasPostCheck() { return true; } @Override public String getReason() { return reason; } @Override protected boolean check(Optional schema) { if (schema.isPresent()) { currentValue.add(SwaggerSpecificationProcessor.extractValue(schema.get())); } return true; } public List getValue() { return expectedValue; } @Override public PropertyValueNotOnlyConditionPredicate setPropertyName(String propertyName) { return (PropertyValueNotOnlyConditionPredicate) super.setPropertyName(propertyName); } @Override public PropertyValueNotOnlyConditionPredicate setMediaTypeName(String mediaTypeName) { return (PropertyValueNotOnlyConditionPredicate) super.setMediaTypeName(mediaTypeName); } public PropertyValueNotOnlyConditionPredicate setValue(List value) { this.expectedValue = value; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/Results.java ================================================ package com.github.viclovsky.swagger.coverage.core.results; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.data.ConditionCounter; import com.github.viclovsky.swagger.coverage.core.results.data.ConditionStatistics; import com.github.viclovsky.swagger.coverage.core.results.data.CoverageCounter; import com.github.viclovsky.swagger.coverage.core.results.data.CoverageOperationMap; import com.github.viclovsky.swagger.coverage.core.results.data.GenerationStatistics; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import com.github.viclovsky.swagger.coverage.core.results.data.TagCoverage; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.info.Info; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; public class Results { private Map operations = new TreeMap<>(); private Map flatOperations = new TreeMap<>(); private Map missed = new TreeMap<>(); private Map deprecated = new TreeMap<>(); private Map conditionStatisticsMap = new HashMap<>(); private Set zeroCall = new HashSet<>(); private GenerationStatistics generationStatistics; private CoverageOperationMap coverageOperationMap = new CoverageOperationMap(); private ConditionCounter conditionCounter = new ConditionCounter(); /** * TAG STATISTICS **/ private Map tagCoverageMap = new TreeMap<>(); private CoverageCounter tagCounter; /** * **/ private String prettyConfiguration; private Info info; public Results() { } public Map getOperations() { return operations; } public Results setOperations(Map operations) { this.operations = operations; return this; } public Set getZeroCall() { return zeroCall; } public Results setZeroCall(Set zeroCall) { this.zeroCall = zeroCall; return this; } public Map getMissed() { return missed; } public Results setMissed(Map missed) { this.missed = missed; return this; } public Results setDeprecated(Map deprecated) { this.deprecated = deprecated; return this; } public Map getDeprecated() { return deprecated; } public GenerationStatistics getGenerationStatistics() { return generationStatistics; } public Results setGenerationStatistics(GenerationStatistics generationStatistics) { this.generationStatistics = generationStatistics; return this; } public ConditionCounter getConditionCounter() { return conditionCounter; } public Results setConditionCounter(ConditionCounter conditionCounter) { this.conditionCounter = conditionCounter; return this; } public Map getConditionStatisticsMap() { return conditionStatisticsMap; } public Results setConditionStatisticsMap(Map conditionStatisticsMap) { this.conditionStatisticsMap = conditionStatisticsMap; return this; } public CoverageOperationMap getCoverageOperationMap() { return coverageOperationMap; } public Results setCoverageOperationMap(CoverageOperationMap coverageOperationMap) { this.coverageOperationMap = coverageOperationMap; return this; } public Map getTagCoverageMap() { return tagCoverageMap; } public Results setTagCoverageMap(Map tagCoverageMap) { this.tagCoverageMap = tagCoverageMap; return this; } public CoverageCounter getTagCounter() { return tagCounter; } public Results setTagCounter(CoverageCounter tagCounter) { this.tagCounter = tagCounter; return this; } public String getPrettyConfiguration() { return prettyConfiguration; } public Results setPrettyConfiguration(String prettyConfiguration) { this.prettyConfiguration = prettyConfiguration; return this; } public Map getFlatOperations() { return flatOperations; } public Results setFlatOperations(Map flatOperations) { this.flatOperations = flatOperations; return this; } public Info getInfo() { return info; } public Results setInfo(Info info) { this.info = info; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/core/StatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.core; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.configuration.options.ConfigurationOptions; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import java.util.List; public abstract class StatisticsBuilder { protected ConfigurationOptions options; public StatisticsBuilder add(String path) { return this; } public StatisticsBuilder add(OpenAPI swagger) { return this; } public StatisticsBuilder configure(ConfigurationOptions options) { this.options = options; return this; } public abstract StatisticsBuilder configure(OpenAPI swagger, List rules); public abstract void build(Results results, Configuration configuration); public abstract boolean isPreBuilder(); public abstract boolean isPostBuilder(); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/core/StatisticsOperationPostBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.core; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import java.util.List; public abstract class StatisticsOperationPostBuilder extends StatisticsPostBuilder { @Override public StatisticsBuilder configure(OpenAPI swagger, List rules) { return this; } @Override public void build(Results results, Configuration configuration) { results.getOperations().forEach(this::buildOperation); buildResult(results); } public abstract void buildOperation(OperationKey operation, OperationResult operationResult); public abstract void buildResult(Results results); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/core/StatisticsPostBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.core; public abstract class StatisticsPostBuilder extends StatisticsBuilder { public StatisticsPostBuilder() { super(); } @Override public boolean isPreBuilder() { return false; } @Override public boolean isPostBuilder() { return true; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/core/StatisticsPreBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.core; public abstract class StatisticsPreBuilder extends StatisticsBuilder { @Override public boolean isPreBuilder() { return true; } @Override public boolean isPostBuilder() { return false; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/postbuilder/ConditionStatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsOperationPostBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.ConditionCounter; import com.github.viclovsky.swagger.coverage.core.results.data.CoverageOperationMap; import com.github.viclovsky.swagger.coverage.core.results.data.CoverageState; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; public class ConditionStatisticsBuilder extends StatisticsOperationPostBuilder { private CoverageOperationMap coverageOperationMap = new CoverageOperationMap(); private ConditionCounter conditionCounter = new ConditionCounter(); @Override public void buildResult(Results results) { results.setCoverageOperationMap(coverageOperationMap) .setConditionCounter(conditionCounter); } @Override public void buildOperation(OperationKey operation, OperationResult operationResult) { conditionCounter.updateAll(operationResult.getAllConditionCount()); conditionCounter.updateCovered(operationResult.getCoveredConditionCount()); switch (operationResult.getState()) { case PARTY: coverageOperationMap.addParty(operation); break; case EMPTY: coverageOperationMap.addEmpty(operation); break; case FULL: coverageOperationMap.addFull(operation); break; } if (operationResult.getDeprecated()) { coverageOperationMap.addDeprecated(operation); conditionCounter.incrementDeprecated(); if (operationResult.getState() == CoverageState.EMPTY) { conditionCounter.incrementDeprecatedAndEmpty(); } } } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/postbuilder/ConfigurationStatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsBuilder; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsPostBuilder; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; public class ConfigurationStatisticsBuilder extends StatisticsPostBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationStatisticsBuilder.class); @Override public StatisticsBuilder configure(OpenAPI swagger, List rules) { return this; } @Override public void build(Results results, Configuration configuration) { ObjectMapper mapper = new ObjectMapper(); String prettyConfiguration = ""; try { prettyConfiguration = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(options); } catch (JsonProcessingException e) { LOGGER.error("can't write options", e); } results.setPrettyConfiguration(prettyConfiguration); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/postbuilder/FlatOperationBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsOperationPostBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import java.util.Map; import java.util.TreeMap; public class FlatOperationBuilder extends StatisticsOperationPostBuilder { @Override public void buildOperation(OperationKey operation, OperationResult operationResult) { } @Override public void buildResult(Results results) { Map flatOperations = new TreeMap<>(); results.getOperations().forEach((operationKey, operationResult) -> flatOperations.put( operationKey.toString(), operationResult) ); results.setFlatOperations(flatOperations); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/postbuilder/SwaggerInfoBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsOperationPostBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import java.util.List; public class SwaggerInfoBuilder extends StatisticsOperationPostBuilder { private Info info; @Override public SwaggerInfoBuilder configure(OpenAPI swagger, List rules) { info = swagger.getInfo(); return this; } @Override public void buildOperation(OperationKey operation, OperationResult operationResult) { } @Override public void buildResult(Results results) { results.setInfo(info); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/postbuilder/TagStatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.model.OperationsHolder; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsOperationPostBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.CoverageCounter; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import com.github.viclovsky.swagger.coverage.core.results.data.TagCoverage; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; import static java.util.Collections.emptyList; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toMap; public class TagStatisticsBuilder extends StatisticsOperationPostBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(TagStatisticsBuilder.class); private Map> operationToTag; private Map tagCoverageMap; private CoverageCounter tagCounter = new CoverageCounter(); @Override public TagStatisticsBuilder configure(OpenAPI swagger, List rules) { OperationsHolder operations = SwaggerSpecificationProcessor.extractOperation(swagger); tagCoverageMap = ofNullable(swagger.getTags()) .orElse(emptyList()) .stream() .collect(toMap(Tag::getName, TagCoverage::new)); operationToTag = operations.getOperations() .entrySet() .stream() .filter(entry -> entry.getValue().getTags() != null) .collect(toMap(Map.Entry::getKey, entry -> entry.getValue().getTags())); operationToTag.forEach((key, value) -> value.stream() .filter(tag -> tagCoverageMap.containsKey(tag)) .forEach(tag -> tagCoverageMap.get(tag).addOperation(key))); return this; } @Override public void buildOperation(OperationKey operation, OperationResult operationResult) { operationToTag.forEach((key, value) -> { if (operation.toString().equals(key.toString())) { value.stream() .filter(tag -> tagCoverageMap.containsKey(tag)) .forEach(tag -> tagCoverageMap.get(tag) .updateCallCount(operationResult.getProcessCount()) .incrementByState(operationResult.getState()) .updateAllConditionCount(operationResult.getAllConditionCount()) .updateCoveredConditionCount(operationResult.getCoveredConditionCount()) .updateState() ); } }); } @Override public void buildResult(Results results) { LOGGER.info(tagCoverageMap.toString()); tagCoverageMap.forEach((key, value) -> tagCounter.incrementByState(value.getState())); results.setTagCoverageMap(tagCoverageMap).setTagCounter(tagCounter); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/postbuilder/ZeroCallStatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.postbuilder; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsOperationPostBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import java.util.HashSet; import java.util.Set; public class ZeroCallStatisticsBuilder extends StatisticsOperationPostBuilder { private Set zeroCall = new HashSet<>(); @Override public void buildOperation(OperationKey operation, OperationResult operationResult) { if (operationResult.getProcessCount() == 0) { zeroCall.add(operation); } } @Override public void buildResult(Results results) { results.setZeroCall(zeroCall); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/prebuilder/CoverageStatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.prebuilder; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.core.generator.OperationConditionGenerator; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.ConditionOperationCoverage; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.model.OperationsHolder; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsPreBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.ConditionStatistics; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.AntPathMatcher; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.function.Predicate; public class CoverageStatisticsBuilder extends StatisticsPreBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(CoverageStatisticsBuilder.class); private Map mainCoverageData; private Map missed = new TreeMap<>(); private Map deprecated = new TreeMap<>(); @Override public CoverageStatisticsBuilder configure(OpenAPI swagger, List rules) { mainCoverageData = OperationConditionGenerator.getOperationMap(swagger, rules); return this; } @Override public CoverageStatisticsBuilder add(OpenAPI swagger) { OperationsHolder operations = SwaggerSpecificationProcessor.extractOperation(swagger); operations.getOperations().forEach((key, value) -> { LOGGER.info(String.format("== process result [%s]", key)); Optional keyOptional = mainCoverageData.keySet().stream() .filter(equalsOperationKeys(key)).findFirst(); if (keyOptional.isPresent()) { mainCoverageData.get(keyOptional.get()) .increaseProcessCount() .getConditions() .stream() .filter(Condition::isNeedCheck) .forEach(condition -> condition.check(value)); } else { LOGGER.info(String.format("Missed request [%s]", key)); missed.put(key, value); } }); return this; } private static Predicate equalsOperationKeys(OperationKey operationKey) { return p -> (p.getHttpMethod() == operationKey.getHttpMethod()) && new AntPathMatcher().match(p.getPath(), operationKey.getPath()); } @Override public void build(Results results, Configuration configuration) { Map operations = new TreeMap<>(); Map conditionStatisticsMap = new HashMap<>(); mainCoverageData.forEach((key, value) -> { value.getConditions().stream().filter(Condition::isHasPostCheck).forEach(Condition::postCheck); operations.put(key, new OperationResult(configuration, value.getConditions(), value.getOperation().getDeprecated()) .setProcessCount(value.getProcessCount()) .setDescription(value.getOperation().getDescription()) .setOperationKey(key) ); if (value.getOperation().getDeprecated() != null && value.getOperation().getDeprecated()) { deprecated.put(key, value.getOperation()); } value.getConditions().forEach(condition -> { if (!conditionStatisticsMap.containsKey(condition.getType())) { conditionStatisticsMap.put(condition.getType(), new ConditionStatistics()); } conditionStatisticsMap.get(condition.getType()).processCondition(key, condition); } ); }); results.setOperations(operations) .setMissed(missed) .setDeprecated(deprecated) .setConditionStatisticsMap(conditionStatisticsMap); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/builder/prebuilder/GenerationStatisticsBuilder.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.builder.prebuilder; import com.github.viclovsky.swagger.coverage.CommandLine; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.builder.core.StatisticsPreBuilder; import com.github.viclovsky.swagger.coverage.core.results.data.GenerationStatistics; import com.github.viclovsky.swagger.coverage.core.results.util.DateTimeUtil; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.OpenAPI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.time.Instant; import java.util.List; public class GenerationStatisticsBuilder extends StatisticsPreBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(CommandLine.class); private long fileCounter = 0; private FileTime minResultTime = null; private FileTime maxResultTime = null; private long startTime; @Override public GenerationStatisticsBuilder configure(OpenAPI swagger, List rules) { startTime = System.currentTimeMillis(); return this; } @Override public GenerationStatisticsBuilder add(String path) { Path file = Paths.get(path); this.fileCounter++; try { BasicFileAttributes attr = Files.readAttributes(file, BasicFileAttributes.class); if (minResultTime == null || minResultTime.toMillis() > attr.lastModifiedTime().toMillis()) { minResultTime = attr.lastModifiedTime(); } if (maxResultTime == null || maxResultTime.toMillis() < attr.lastModifiedTime().toMillis()) { maxResultTime = attr.lastModifiedTime(); } } catch (IOException e) { LOGGER.error("can't read file attributes", e); } return this; } @Override public void build(Results results, Configuration configuration) { final long duration = System.currentTimeMillis() - startTime; final String resultDateDuration = DateTimeUtil.formatDate(minResultTime.toInstant()) + " - " + DateTimeUtil.formatDate(maxResultTime.toInstant()); results.setGenerationStatistics( new GenerationStatistics() .setResultFileCount(fileCounter) .setGenerationTime(duration) .setFileResultDateInterval(resultDateDuration) .setGenerateDate(DateTimeUtil.formatDate(Instant.now())) ); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/ConditionCounter.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; public class ConditionCounter { private long all = 0; private long covered = 0; private long deprecated = 0; private long deprecatedAndEmpty = 0; public ConditionCounter updateAll(long count) { this.all = this.all + count; return this; } public ConditionCounter updateCovered(long count) { this.covered = this.covered + count; return this; } public ConditionCounter incrementDeprecated() { this.deprecated = this.deprecated + 1; return this; } public ConditionCounter incrementDeprecatedAndEmpty() { this.deprecatedAndEmpty = this.deprecatedAndEmpty + 1; return this; } public long getAll() { return all; } public ConditionCounter setAll(long all) { this.all = all; return this; } public long getCovered() { return covered; } public ConditionCounter setCovered(long covered) { this.covered = covered; return this; } public long getDeprecated() { return deprecated; } public void setDeprecated(long deprecated) { this.deprecated = deprecated; } public long getDeprecatedAndEmpty() { return deprecatedAndEmpty; } public void setDeprecatedAndEmpty(long deprecatedAndEmpty) { this.deprecatedAndEmpty = deprecatedAndEmpty; } @Override public String toString() { return "ConditionCounter{" + "all=" + all + ", covered=" + covered + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/ConditionStatistics.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import java.util.ArrayList; import java.util.List; public class ConditionStatistics { private long allCount = 0; private long coveredCount = 0; private long uncoveredCount = 0; private List coveredOperation = new ArrayList<>(); private List uncoveredOperation = new ArrayList<>(); public long getAllCount() { return allCount; } public ConditionStatistics setAllCount(long allCount) { this.allCount = allCount; return this; } public long getCoveredCount() { return coveredCount; } public ConditionStatistics setCoveredCount(long coveredCount) { this.coveredCount = coveredCount; return this; } public long getUncoveredCount() { return uncoveredCount; } public ConditionStatistics setUncoveredCount(long uncoveredCount) { this.uncoveredCount = uncoveredCount; return this; } public List getCoveredOperation() { return coveredOperation; } public ConditionStatistics setCoveredOperation(List coveredOperation) { this.coveredOperation = coveredOperation; return this; } public List getUncoveredOperation() { return uncoveredOperation; } public ConditionStatistics setUncoveredOperation(List uncoveredOperation) { this.uncoveredOperation = uncoveredOperation; return this; } public ConditionStatistics processCondition(OperationKey operation, Condition condition) { this.allCount++; if (condition.isCovered()) { this.coveredCount++; this.coveredOperation.add(new ConditionStatisticsItem(operation, condition)); } else { this.uncoveredCount++; this.uncoveredOperation.add(new ConditionStatisticsItem(operation, condition)); ; } return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/ConditionStatisticsItem.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; public class ConditionStatisticsItem { private OperationKey operation; private Condition condition; public ConditionStatisticsItem(OperationKey operation, Condition condition) { this.operation = operation; this.condition = condition; } public OperationKey getOperation() { return operation; } public ConditionStatisticsItem setOperation(OperationKey operation) { this.operation = operation; return this; } public Condition getCondition() { return condition; } public ConditionStatisticsItem setCondition(Condition condition) { this.condition = condition; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/CoverageCounter.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; public class CoverageCounter { private long all = 0; private long full = 0; private long party = 0; private long empty = 0; private long deprecated = 0; public CoverageCounter incrementByState(CoverageState state) { switch (state) { case FULL: return incrementFull(); case EMPTY: return incrementEmpty(); case PARTY: return incrementParty(); } return this; } public CoverageCounter incrementFull() { this.all++; this.full++; return this; } public CoverageCounter incrementParty() { this.all++; this.party++; return this; } public CoverageCounter incrementEmpty() { this.all++; this.empty++; return this; } public CoverageCounter incrementDeprecated() { this.deprecated++; return this; } public long getAll() { return all; } public CoverageCounter setAll(long all) { this.all = all; return this; } public long getFull() { return full; } public CoverageCounter setFull(long full) { this.full = full; return this; } public long getParty() { return party; } public CoverageCounter setParty(long party) { this.party = party; return this; } public long getEmpty() { return empty; } public CoverageCounter setEmpty(long empty) { this.empty = empty; return this; } public long getDeprecated() { return deprecated; } public CoverageCounter setDeprecated(long deprecated) { this.deprecated = deprecated; return this; } @Override public String toString() { return "CoverageCounter{" + "all=" + all + ", full=" + full + ", party=" + party + ", empty=" + empty + ", deprecated=" + deprecated + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/CoverageOperationMap.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import java.util.HashSet; import java.util.Set; public class CoverageOperationMap { private Set full = new HashSet<>(); private Set party = new HashSet<>(); private Set empty = new HashSet<>(); private Set deprecated = new HashSet<>(); protected CoverageCounter counter = new CoverageCounter(); public CoverageOperationMap addFull(OperationKey operation) { this.full.add(operation); this.counter.incrementFull(); return this; } public CoverageOperationMap addParty(OperationKey operation) { this.party.add(operation); this.counter.incrementParty(); return this; } public CoverageOperationMap addEmpty(OperationKey operation) { this.empty.add(operation); this.counter.incrementEmpty(); return this; } public CoverageOperationMap addDeprecated(OperationKey operation) { this.deprecated.add(operation); this.counter.incrementDeprecated(); return this; } public Set getFull() { return full; } public CoverageOperationMap setFull(Set full) { this.full = full; return this; } public Set getParty() { return party; } public CoverageOperationMap setParty(Set party) { this.party = party; return this; } public Set getEmpty() { return empty; } public CoverageOperationMap setEmpty(Set empty) { this.empty = empty; return this; } public Set getDeprecated() { return deprecated; } public CoverageOperationMap setDeprecated(Set deprecated) { this.deprecated = deprecated; return this; } public CoverageCounter getCounter() { return counter; } public CoverageOperationMap setCounter(CoverageCounter counter) { this.counter = counter; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/CoverageState.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; public enum CoverageState { FULL, PARTY, EMPTY, DEPRECATED, } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/GenerationStatistics.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; public class GenerationStatistics { private long resultFileCount = 0; private long generationTime; private String fileResultDateInterval; private String generateDate; public GenerationStatistics() { } public long getResultFileCount() { return resultFileCount; } public GenerationStatistics setResultFileCount(long resultFileCount) { this.resultFileCount = resultFileCount; return this; } public long getGenerationTime() { return generationTime; } public GenerationStatistics setGenerationTime(long generationTime) { this.generationTime = generationTime; return this; } public String getFileResultDateInterval() { return fileResultDateInterval; } public GenerationStatistics setFileResultDateInterval(String fileResultDateInterval) { this.fileResultDateInterval = fileResultDateInterval; return this; } public String getGenerateDate() { return generateDate; } public GenerationStatistics setGenerateDate(String generateDate) { this.generateDate = generateDate; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/OperationResult.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; import com.github.viclovsky.swagger.coverage.configuration.Configuration; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import java.util.List; public class OperationResult { private boolean excludeDeprecated; private OperationKey operationKey; private List conditions; private long allConditionCount; private long coveredConditionCount; private long processCount; private String description; private CoverageState state; private boolean deprecated; public OperationResult(Configuration configuration, List conditions, Boolean isDeprecated) { this.conditions = conditions; this.deprecated = (isDeprecated != null) ? isDeprecated : false; allConditionCount = conditions.size(); coveredConditionCount = conditions.stream().filter(Condition::isCovered).count(); try { excludeDeprecated = configuration.getOption("exclude-deprecated").isEnable(); } catch (NullPointerException e) { excludeDeprecated = false; } if (this.deprecated && excludeDeprecated) { state = CoverageState.DEPRECATED; } else if (coveredConditionCount == 0) { state = CoverageState.EMPTY; } else { if (allConditionCount == coveredConditionCount) { state = CoverageState.FULL; } else { state = CoverageState.PARTY; } } } public long getAllConditionCount() { return allConditionCount; } public OperationResult setAllConditionCount(long allConditionCount) { this.allConditionCount = allConditionCount; return this; } public long getCoveredConditionCount() { return coveredConditionCount; } public OperationResult setCoveredConditionCount(long coveredConditionCount) { this.coveredConditionCount = coveredConditionCount; return this; } public long getProcessCount() { return processCount; } public OperationResult setProcessCount(long processCount) { this.processCount = processCount; return this; } public String getDescription() { if (description == null) { return ""; } return description; } public OperationResult setDescription(String description) { this.description = description; return this; } public CoverageState getState() { return state; } public OperationResult setState(CoverageState state) { this.state = state; return this; } public OperationKey getOperationKey() { return operationKey; } public OperationResult setOperationKey(OperationKey operationKey) { this.operationKey = operationKey; return this; } public List getConditions() { return conditions; } public void setConditions(List conditions) { this.conditions = conditions; } public boolean getDeprecated() { return deprecated; } public void setDeprecated(boolean isDeprecated) { this.deprecated = isDeprecated; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/data/TagCoverage.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.data; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import io.swagger.v3.oas.models.tags.Tag; import java.util.HashSet; import java.util.Set; public class TagCoverage { private Tag tag; private Set operations = new HashSet<>(); private CoverageCounter coverageCounter = new CoverageCounter(); private ConditionCounter conditionCounter = new ConditionCounter(); private long callCounts = 0; private CoverageState state = CoverageState.EMPTY; public TagCoverage(Tag tag) { this.tag = tag; } public TagCoverage updateState() { if (conditionCounter.getCovered() == 0) { state = CoverageState.EMPTY; } else { if (conditionCounter.getAll() == conditionCounter.getCovered()) { state = CoverageState.FULL; } else { state = CoverageState.PARTY; } } return this; } public TagCoverage addOperation(OperationKey operation) { this.operations.add(operation); return this; } public TagCoverage updateCallCount(long callCounts) { this.callCounts += callCounts; return this; } public TagCoverage incrementByState(CoverageState state) { this.coverageCounter.incrementByState(state); return this; } public TagCoverage updateAllConditionCount(long count) { conditionCounter.updateAll(count); return this; } public TagCoverage updateCoveredConditionCount(long count) { conditionCounter.updateCovered(count); return this; } public Tag getTag() { return tag; } public TagCoverage setTag(Tag tag) { this.tag = tag; return this; } public CoverageCounter getCoverageCounter() { return coverageCounter; } public TagCoverage setCoverageCounter(CoverageCounter coverageCounter) { this.coverageCounter = coverageCounter; return this; } public Set getOperations() { return operations; } public TagCoverage setOperations(Set operations) { this.operations = operations; return this; } public ConditionCounter getConditionCounter() { return conditionCounter; } public TagCoverage setConditionCounter(ConditionCounter conditionCounter) { this.conditionCounter = conditionCounter; return this; } public long getCallCounts() { return callCounts; } public TagCoverage setCallCounts(long callCounts) { this.callCounts = callCounts; return this; } public CoverageState getState() { return state; } public TagCoverage setState(CoverageState state) { this.state = state; return this; } @Override public String toString() { return "TagCoverage{" + "tag='" + tag.getName() + '\'' + ", operations=" + operations.toString() + ", coverageCounter=" + coverageCounter.toString() + ", conditionCounter=" + conditionCounter.toString() + '}'; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/results/util/DateTimeUtil.java ================================================ package com.github.viclovsky.swagger.coverage.core.results.util; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; public class DateTimeUtil { private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter .ofPattern("yyyy-MM-dd HH:mm:ss") .withZone(ZoneId.systemDefault()); public static String formatDate(Instant instant) { return dateTimeFormatter.format(instant); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/body/NotEmptyBodyRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.body; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.DefaultBodyConditionPredicate; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.Operation; import java.util.ArrayList; import java.util.List; public class NotEmptyBodyRule extends ConditionRule { @Override public List createCondition(Operation operation) { if (operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) { List conditions = new ArrayList<>(); conditions.add(new SinglePredicateCondition( "not empty body request", "", new DefaultBodyConditionPredicate() )); return conditions; } else { return null; } } @Override public String getId() { return "not-empty-body"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/body/PropertyConditionRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.body; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; //request body properties are the v3 version of form data params public abstract class PropertyConditionRule extends ConditionRule { public List createCondition(Operation operation) { if (operation.getRequestBody() != null && operation.getRequestBody().getContent() != null) { return operation.getRequestBody().getContent().entrySet().stream() .flatMap(m -> processMediaType(m.getKey(), m.getValue())) .filter(Objects::nonNull) .collect(Collectors.toList()); } else { return null; } } private Stream processMediaType(String mediaTypeName, MediaType mediaType) { if (mediaType.getSchema() != null && mediaType.getSchema().getProperties() != null) { return ((Set>) mediaType.getSchema().getProperties().entrySet()) .stream() .map(s -> processProperty(mediaTypeName, s.getKey(), s.getValue())) .filter(Objects::nonNull); } else { return null; } } protected abstract Condition processProperty(String mediaTypeName, String name, Schema schema); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/body/PropertyEnumAllValuesRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.body; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.PropertyValueConditionPredicate; import io.swagger.v3.oas.models.media.Schema; import java.util.List; public class PropertyEnumAllValuesRule extends PropertyConditionRule { @Override protected Condition processProperty(String mediaTypeName, String name, Schema schema) { List enums = SwaggerSpecificationProcessor.extractEnum(schema); if (schema != null && name != null && mediaTypeName != null && enums != null && !enums.isEmpty()) { return new SinglePredicateCondition( String.format("«%s» contains all values from enum %s", name, enums), "", new PropertyValueConditionPredicate(mediaTypeName, name, enums) ); } return null; } @Override public String getId() { return "property-enum-all-value"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/body/PropertyNotEmptyRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.body; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.DefaultPropertyConditionPredicate; import io.swagger.v3.oas.models.media.Schema; public class PropertyNotEmptyRule extends PropertyConditionRule { @Override protected Condition processProperty(String mediaTypeName, String name, Schema schema) { if (schema != null && name != null && mediaTypeName != null) { return new SinglePredicateCondition( String.format("«%s» is not empty", name), "", new DefaultPropertyConditionPredicate(mediaTypeName, name, false) ); } return null; } @Override public String getId() { return "property-not-empty"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/body/PropertyNotOnlyEnumValuesRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.body; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.PropertyValueNotOnlyConditionPredicate; import io.swagger.v3.oas.models.media.Schema; import java.util.List; public class PropertyNotOnlyEnumValuesRule extends PropertyConditionRule { @Override protected Condition processProperty(String mediaTypeName, String name, Schema schema) { List enums = SwaggerSpecificationProcessor.extractEnum(schema); if (schema != null && name != null && mediaTypeName != null && enums != null && !enums.isEmpty()) { return new SinglePredicateCondition( String.format("«%s» contains all values from enum %s", name, enums), "", new PropertyValueNotOnlyConditionPredicate(mediaTypeName, name, enums) ); } return null; } @Override public String getId() { return "property-enum-another-value"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/core/ConditionRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.core; import com.github.viclovsky.swagger.coverage.configuration.options.RuleConfigurationOptions; import com.github.viclovsky.swagger.coverage.core.model.Condition; import io.swagger.v3.oas.models.Operation; import java.util.List; public abstract class ConditionRule { protected RuleConfigurationOptions options; public abstract String getId(); public abstract List createCondition(Operation operation); public ConditionRule configure(RuleConfigurationOptions options) { this.options = options; return this; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/parameter/EmptyHeaderRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.parameter; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import com.github.viclovsky.swagger.coverage.core.predicate.DefaultParameterConditionPredicate; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.Parameter; public class EmptyHeaderRule extends ParameterConditionRule { @Override public Condition processParameter(Parameter parameter) { if (parameter instanceof HeaderParameter) { ConditionPredicate predicate = new DefaultParameterConditionPredicate(true, parameter.getName(), parameter.getIn()); return new SinglePredicateCondition( String.format("header «%s» is empty", parameter.getName()), "", predicate ); } return null; } @Override public String getId() { return "empty-required-header"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/parameter/EnumAllValuesRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.parameter; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import com.github.viclovsky.swagger.coverage.core.predicate.ParameterValueConditionPredicate; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.List; public class EnumAllValuesRule extends ParameterConditionRule { @Override public Condition processParameter(Parameter parameter) { List enumValues = SwaggerSpecificationProcessor.extractEnum(parameter); if (enumValues != null && !enumValues.isEmpty()) { ConditionPredicate predicate = new ParameterValueConditionPredicate(parameter.getName(), parameter.getIn(), enumValues); return new SinglePredicateCondition( String.format("%s «%s» contains all values from enum %s", parameter.getIn(), parameter.getName(), enumValues), "", predicate ); } return null; } @Override public String getId() { return "enum-all-value"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/parameter/NotEmptyParameterRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.parameter; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import com.github.viclovsky.swagger.coverage.core.predicate.DefaultParameterConditionPredicate; import io.swagger.v3.oas.models.parameters.Parameter; public class NotEmptyParameterRule extends ParameterConditionRule { @Override public Condition processParameter(Parameter parameter) { ConditionPredicate predicate = new DefaultParameterConditionPredicate(false, parameter.getName(), parameter.getIn()); return new SinglePredicateCondition( String.format("%s «%s» is not empty", parameter.getIn(), parameter.getName()), "", predicate ); } @Override public String getId() { return "parameter-not-empty"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/parameter/NotOnlyEnumValuesRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.parameter; import com.github.viclovsky.swagger.coverage.core.generator.SwaggerSpecificationProcessor; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import com.github.viclovsky.swagger.coverage.core.predicate.NotOnlyParameterListValueConditionPredicate; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.List; public class NotOnlyEnumValuesRule extends ParameterConditionRule { @Override public Condition processParameter(Parameter parameter) { List enumValues = SwaggerSpecificationProcessor.extractEnum(parameter); if (enumValues != null && !enumValues.isEmpty()) { ConditionPredicate predicate = new NotOnlyParameterListValueConditionPredicate( parameter.getName(), parameter.getIn(), enumValues ); return new SinglePredicateCondition( String.format("%s «%s» contains values not only from enum", parameter.getIn(), parameter.getName()), "", predicate ); } return null; } @Override public String getId() { return "enum-another-value"; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/parameter/ParameterConditionRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.parameter; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; public abstract class ParameterConditionRule extends ConditionRule { public abstract Condition processParameter(Parameter parameter); public List createCondition(Operation operation) { if(operation.getParameters() != null) { return operation .getParameters() .stream() .map(this::processParameter) .filter(Objects::nonNull) .collect(Collectors.toList()); } else { return null; } } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/status/HTTPStatusRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.status; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import com.github.viclovsky.swagger.coverage.core.predicate.DefaultStatusConditionPredicate; public class HTTPStatusRule extends StatusConditionRule { @Override public String getId() { return "status"; } @Override public Condition processStatus(String status) { if (skip(status)) { return null; } ConditionPredicate predicate = new DefaultStatusConditionPredicate(status); return new SinglePredicateCondition( "HTTP status " + status, "", predicate ); } protected boolean skip(String status) { if (this.options == null) { return false; } if (this.options.getFilter() != null && !this.options.getFilter().isEmpty() && !this.options.getFilter().contains(status) ) { return true; } return this.options.getIgnore() != null && !this.options.getIgnore().isEmpty() && this.options.getIgnore().contains(status); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/status/OnlyDeclaredHTTPStatusesRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.status; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.model.SinglePredicateCondition; import com.github.viclovsky.swagger.coverage.core.predicate.ConditionPredicate; import com.github.viclovsky.swagger.coverage.core.predicate.FullStatusConditionPredicate; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.Operation; import java.util.Collections; import java.util.List; public class OnlyDeclaredHTTPStatusesRule extends ConditionRule { @Override public String getId() { return "only-declared-status"; } @Override public List createCondition(Operation operation) { ConditionPredicate predicate = new FullStatusConditionPredicate(operation.getResponses().keySet()); Condition condition = new SinglePredicateCondition( "only declared status", "", predicate ); return Collections.singletonList(condition); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/rule/status/StatusConditionRule.java ================================================ package com.github.viclovsky.swagger.coverage.core.rule.status; import com.github.viclovsky.swagger.coverage.core.model.Condition; import com.github.viclovsky.swagger.coverage.core.rule.core.ConditionRule; import io.swagger.v3.oas.models.Operation; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * Base rule for status */ public abstract class StatusConditionRule extends ConditionRule { public abstract Condition processStatus(String statusCode); public List createCondition(Operation operation) { return operation.getResponses() .keySet() .stream() .map(this::processStatus) .filter(Objects::nonNull) .collect(Collectors.toList()); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/writer/CoverageResultsWriter.java ================================================ package com.github.viclovsky.swagger.coverage.core.writer; import com.github.viclovsky.swagger.coverage.core.results.Results; public interface CoverageResultsWriter { void write(Results results); } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/writer/FileSystemResultsWriter.java ================================================ package com.github.viclovsky.swagger.coverage.core.writer; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.viclovsky.swagger.coverage.SwaggerCoverageWriteException; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.model.SwaggerCoverage2ModelJackson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.COVERAGE_RESULTS_NAME; public class FileSystemResultsWriter implements CoverageResultsWriter { private final static Logger LOGGER = LoggerFactory.getLogger(FileSystemResultsWriter.class); private final ObjectMapper mapper; private final String fileName; public FileSystemResultsWriter() { this(COVERAGE_RESULTS_NAME); } public FileSystemResultsWriter(String fileName) { this.fileName = fileName; this.mapper = SwaggerCoverage2ModelJackson.createJsonMapper(); } @Override public void write(Results results) { final String swaggerResultName = fileName; Path path = Paths.get(swaggerResultName); LOGGER.info(String.format("Write results in file '%s'", path.toAbsolutePath())); try (OutputStream os = Files.newOutputStream(Paths.get(swaggerResultName))) { mapper.writeValue(os, results); } catch (IOException e) { throw new SwaggerCoverageWriteException("Could not write results", e); } } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/writer/HtmlReportResultsWriter.java ================================================ package com.github.viclovsky.swagger.coverage.core.writer; import com.github.viclovsky.swagger.coverage.SwaggerCoverageWriteException; import com.github.viclovsky.swagger.coverage.configuration.options.ResultsWriterOptions; import com.github.viclovsky.swagger.coverage.core.results.Results; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import static com.github.viclovsky.swagger.coverage.utils.FreemarkerUtils.processCustomTemplate; import static com.github.viclovsky.swagger.coverage.utils.FreemarkerUtils.processTemplate; public class HtmlReportResultsWriter implements CoverageResultsWriter { private static final Logger LOGGER = LoggerFactory.getLogger(HtmlReportResultsWriter.class); private ResultsWriterOptions options; private String filename = "swagger-coverage-report.html"; private String localeCode = "en"; private String numberFormat = "0.###"; public HtmlReportResultsWriter() { options = new ResultsWriterOptions() .setFilename(filename) .setLocale(localeCode) .setNumberFormat(numberFormat); } public HtmlReportResultsWriter(ResultsWriterOptions options){ if (options.getLocale() == null){ options.setLocale(localeCode); } if (options.getFilename() == null){ options.setFilename(filename); } if (options.getNumberFormat() == null){ options.setNumberFormat(numberFormat); } this.options = options; } @Override public void write(Results results) { Path path = Paths.get(options.getFilename()); LOGGER.info(String.format("Write html report in file '%s'", path.toAbsolutePath())); try { final String htmlReport = (options.getCustomTemplatePath() == null ) ? processTemplate("report.ftl", options.getLocale(), options.getNumberFormat(), results) : processCustomTemplate(options.getCustomTemplatePath(), options.getLocale(), options.getNumberFormat(), results); Files.write(Paths.get(options.getFilename()), htmlReport.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new SwaggerCoverageWriteException("Could not write results", e); } } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/core/writer/LogResultsWriter.java ================================================ package com.github.viclovsky.swagger.coverage.core.writer; import com.github.viclovsky.swagger.coverage.core.model.OperationKey; import com.github.viclovsky.swagger.coverage.core.results.Results; import com.github.viclovsky.swagger.coverage.core.results.data.OperationResult; import io.swagger.v3.oas.models.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.DecimalFormat; import java.util.Map; import java.util.Set; public class LogResultsWriter implements CoverageResultsWriter { private final static Logger LOGGER = LoggerFactory.getLogger(LogResultsWriter.class); public LogResultsWriter() { } @Override public void write(Results results) { LOGGER.info("Deprecated coverage: "); logOperationCoverage(results.getOperations(), results.getCoverageOperationMap().getDeprecated()); LOGGER.info("Empty coverage: "); logOperationCoverage(results.getOperations(), results.getCoverageOperationMap().getEmpty()); LOGGER.info("Partial coverage: "); logOperationCoverage(results.getOperations(), results.getCoverageOperationMap().getParty()); LOGGER.info("Full coverage: "); logOperationCoverage(results.getOperations(), results.getCoverageOperationMap().getFull()); logMissedCoverage(results.getMissed()); DecimalFormat df = new DecimalFormat("###.###"); float deprecatedPercentage = (float) (results.getCoverageOperationMap().getDeprecated().size() * 100) / results.getOperations().size(); float emptyPercentage = (float) (results.getCoverageOperationMap().getEmpty().size() * 100) / results.getOperations().size(); float partialPercentage = (float) (results.getCoverageOperationMap().getParty().size() * 100) / results.getOperations().size(); float fullPercentage = (float) (results.getCoverageOperationMap().getFull().size() * 100) / results.getOperations().size(); LOGGER.info(String.format("Conditions: %s/%s", results.getConditionCounter().getCovered(), results.getConditionCounter().getAll())); LOGGER.info("Deprecated coverage " + df.format(deprecatedPercentage) + " %"); LOGGER.info("Empty coverage " + df.format(emptyPercentage) + " %"); LOGGER.info("Partial coverage " + df.format(partialPercentage) + " %"); LOGGER.info("Full coverage " + df.format(fullPercentage) + " %"); } private void logMissedCoverage(Map missed) { if (!missed.isEmpty()) { LOGGER.info("Missed coverage: "); missed.keySet().forEach( m -> LOGGER.info(m.getHttpMethod() + " " + m.getPath())); } } private void logOperationCoverage(Map operationResults, Set keys) { keys.forEach(operationKey -> { if (operationResults.containsKey(operationKey)) { printOperationCoverage(operationResults.get(operationKey)); } }); } private void printOperationCoverage(OperationResult result) { LOGGER.info(String.format("%s %s (%s/%s)", result.getOperationKey().getHttpMethod(), result.getOperationKey().getPath(), result.getCoveredConditionCount(), result.getAllConditionCount())); result.getConditions().forEach(c -> { if (c.isCovered()) { LOGGER.info(String.format("✅ %s", c.getName())); } else { LOGGER.info(String.format("❌ %s", c.getName())); } }); } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/option/MainOptions.java ================================================ package com.github.viclovsky.swagger.coverage.option; import com.beust.jcommander.Parameter; import java.net.URI; import java.nio.file.Path; public class MainOptions { @Parameter( names = {"-s", "--spec"}, description = "Path to local or URL to remote swagger specification.", required = true, order = 0 ) private URI specPath; @Parameter( names = {"-i", "--input"}, description = "Path to folder with generated files with coverage.", required = true, order = 1 ) private Path inputPath; @Parameter( names = {"-c", "--configuration"}, description = "Path to file with report configuration.", order = 1 ) private Path configuration; @Parameter( names = "--help", description = "Print commandline help.", help = true, order = 5 ) private boolean help; public boolean isHelp() { return help; } public URI getSpecPath() { return specPath; } public Path getInputPath() { return inputPath; } public Path getConfiguration() { return configuration; } } ================================================ FILE: swagger-coverage-commandline/src/main/java/com/github/viclovsky/swagger/coverage/option/VerboseOptions.java ================================================ package com.github.viclovsky.swagger.coverage.option; import com.beust.jcommander.Parameter; public class VerboseOptions { @Parameter( names = {"-v", "--verbose"}, description = "Switch on the verbose mode." ) private boolean verbose; @Parameter( names = {"-q", "--quiet"}, description = "Switch on the quiet mode." ) private boolean quiet; /** * Returns true if silent mode is enabled, false otherwise. */ public boolean isQuiet() { return quiet; } /** * Returns true if verbose mode is enabled, false otherwise. */ public boolean isVerbose() { return verbose; } } ================================================ FILE: swagger-coverage-commandline/src/main/resources/logback.xml ================================================ System.out %d{HH:mm:ss} %-5p %c{1} - %m%n ================================================ FILE: swagger-coverage-commandline/src/test/java/com/github/viclovsky/swagger/coverage/Config.java ================================================ package com.github.viclovsky.swagger.coverage; import java.io.File; import java.net.URI; import java.nio.file.Path; import static java.util.Optional.ofNullable; public class Config { private final String path; private final String outputPath; private final String specPath; public Config(String path, String outputPath, String specPath) { this.path = path; this.outputPath = outputPath; this.specPath = specPath; } public Path getPath() { return getFile(path).toPath(); } public Path getOutput() { return getFile(outputPath).toPath(); } public URI getSpec() { return URI.create(specPath); } private File getFile(String name) { return ofNullable(getClass().getClassLoader().getResource(name)) .map(spec -> new File(spec.getFile())) .orElseThrow(() -> new IllegalArgumentException("Unable to read file: " + name)); } } ================================================ FILE: swagger-coverage-commandline/src/test/java/com/github/viclovsky/swagger/coverage/CustomReportTemplateTest.java ================================================ package com.github.viclovsky.swagger.coverage; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.viclovsky.swagger.coverage.configuration.options.ConfigurationOptions; import com.github.viclovsky.swagger.coverage.configuration.options.ResultsWriterOptions; import com.github.viclovsky.swagger.coverage.core.generator.Generator; import io.swagger.v3.parser.core.models.ParseOptions; import org.apache.commons.io.FileUtils; import org.hamcrest.io.FileMatchers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import static java.nio.file.Paths.get; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.text.StringContainsInOrder.stringContainsInOrder; @RunWith(Parameterized.class) public class CustomReportTemplateTest { private static final Logger LOGGER = LoggerFactory.getLogger(CustomReportTemplateTest.class); private final String outputPath, specPath; public CustomReportTemplateTest(String outputPath, String specPath ) { this.outputPath = outputPath; this.specPath = specPath; } private File generateConfigurationFile() throws URISyntaxException, IOException { HashMap customReportOptions = new HashMap<>(); URL res = getClass().getClassLoader().getResource("report_custom.ftl"); customReportOptions.put("html", new ResultsWriterOptions() .setFilename("custom-template-report.html") .setLocale("en") .setNumberFormat("0.###") .setCustomTemplatePath(Paths.get(res.toURI()).toFile().getAbsolutePath())); ParseOptions parseOptions = new ParseOptions(); parseOptions.setResolve(true); ConfigurationOptions configurationOptions = new ConfigurationOptions() .setWriters(customReportOptions); File testConfigurationFile = File.createTempFile("customTemplate", ".json"); FileUtils.writeStringToFile(testConfigurationFile, new ObjectMapper().writeValueAsString(configurationOptions)); return testConfigurationFile; } @Test public void testWithCustomTemplate() throws IOException, URISyntaxException { File testConfigurationFile = generateConfigurationFile(); Config testConfig = new Config(testConfigurationFile.getAbsolutePath(), outputPath, specPath); LOGGER.info("Generate report for {}:", testConfigurationFile.getAbsolutePath()); LOGGER.info("{}", FileUtils.readFileToString(testConfigurationFile)); new Generator() .setInputPath(testConfig.getOutput()) .setSpecPath(testConfig.getSpec()) .setConfigurationPath(testConfigurationFile.toPath()) .run(); File reportFile = get("custom-template-report.html").toFile(); assertThat(reportFile, FileMatchers.anExistingFile()); assertThat(FileUtils.readFileToString(reportFile), stringContainsInOrder("CUSTOM_TEST_REPORT")); } @Parameterized.Parameters() public static Collection testData() { return Arrays.asList(new Object[][]{ //Swagger v2 {"v2/swagger-coverage-output", "v2/petstory.json"}, //Swagger v3 {"v3/swagger-coverage-output", "v3/petstory.yaml"}, }); } } ================================================ FILE: swagger-coverage-commandline/src/test/java/com/github/viclovsky/swagger/coverage/ParseOptionsTest.java ================================================ package com.github.viclovsky.swagger.coverage; import com.github.viclovsky.swagger.coverage.core.generator.Generator; import org.junit.Test; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; import static java.nio.file.Paths.get; import static org.hamcrest.MatcherAssert.assertThat; public class ParseOptionsTest { private static final String V3_OUTPUT_SWAGGER_COVERAGE_DIR = "v3/swagger-coverage-output"; private static final String CONFIGURATION_FILE = "full_configuration.json"; @Test public void resolveOptionTest() { Config config = new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "v3/petstory_ref_operations.yaml"); new Generator() .setInputPath(config.getOutput()) .setSpecPath(config.getSpec()) .setConfigurationPath(config.getPath()) .run(); assertThat(get("json-report.json").toFile(), hasJsonPath("$.operations.[\"/pet/{petId} GET\"]")); } } ================================================ FILE: swagger-coverage-commandline/src/test/java/com/github/viclovsky/swagger/coverage/SimpleTest.java ================================================ package com.github.viclovsky.swagger.coverage; import com.github.viclovsky.swagger.coverage.core.generator.Generator; import org.hamcrest.io.FileMatchers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.COVERAGE_HTML_REPORT_NAME; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.COVERAGE_RESULTS_NAME; import static java.nio.file.Paths.get; import static org.hamcrest.MatcherAssert.assertThat; @RunWith(Parameterized.class) public class SimpleTest { private static final String V2_OUTPUT_SWAGGER_COVERAGE_DIR = "v2/swagger-coverage-output"; private static final String V3_OUTPUT_SWAGGER_COVERAGE_DIR = "v3/swagger-coverage-output"; private static final String CONFIGURATION_FILE = "full_configuration.json"; private final Config config; public SimpleTest(Config config) { this.config = config; } @Parameterized.Parameters() public static Collection testData() { return Arrays.asList(new Object[][]{ //Swagger v2 {new Config(CONFIGURATION_FILE, V2_OUTPUT_SWAGGER_COVERAGE_DIR, "v2/petstory.json")}, {new Config(CONFIGURATION_FILE, V2_OUTPUT_SWAGGER_COVERAGE_DIR, "v2/petstory_no_tags.json")}, {new Config(CONFIGURATION_FILE, V2_OUTPUT_SWAGGER_COVERAGE_DIR, "v2/petstory_operation_wo_tags.json")}, {new Config(CONFIGURATION_FILE, V2_OUTPUT_SWAGGER_COVERAGE_DIR, "v2/petstory_with_x_example.json")}, {new Config(CONFIGURATION_FILE, V2_OUTPUT_SWAGGER_COVERAGE_DIR, "v2/petstory_without_parameters.json")}, {new Config(CONFIGURATION_FILE, V2_OUTPUT_SWAGGER_COVERAGE_DIR, "https://petstore.swagger.io/v2/swagger.json")}, //Swagger v3 {new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "v3/petstory.yaml")}, {new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "v3/petstory_no_tags.yaml")}, {new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "v3/petstory_operation_wo_tags.yaml")}, {new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "v3/petstory_with_x_example.yaml")}, {new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "v3/petstory_without_parameters.yaml")}, {new Config(CONFIGURATION_FILE, V3_OUTPUT_SWAGGER_COVERAGE_DIR, "https://petstore3.swagger.io/api/v3/openapi.yaml")} }); } @Test public void simpleTest() { new Generator() .setInputPath(config.getOutput()) .setSpecPath(config.getSpec()) .run(); assertThat(get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); assertThat(get(COVERAGE_RESULTS_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void simpleTestWithConfiguration() { new Generator() .setInputPath(config.getOutput()) .setSpecPath(config.getSpec()) .setConfigurationPath(config.getPath()) .run(); assertThat(get("custom-report.html").toFile(), FileMatchers.anExistingFile()); assertThat(get("json-report.json").toFile(), FileMatchers.anExistingFile()); } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/configuration.json ================================================ { "rules" : { "status": { "filter": ["200"] }, "only-declared-status" : { "enable" : false }, "empty-required-header" : { "enable" : false }, "enum-another-value" : { "enable" : false } }, "writers": { "html": { "filename": "custom-report.html", "locale": "en" } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/full_configuration.json ================================================ { "rules": { "status": { "filter": [ "200" ], "ignore": [ "500", "400", "404" ] }, "only-declared-status": { "enable": true }, "empty-required-header": { "enable": true }, "enum-another-value": { "enable": true }, "exclude-deprecated" : { "enable" : true } }, "writers": { "html": { "filename": "custom-report.html", "locale": "en" }, "json": { "filename": "json-report.json" } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/report_custom.ftl ================================================ <#ftl output_format="HTML"> <#global i18=messages> <#global operationMap=data.flatOperations> <#-- @ftlvariable ftlvariable name="data" type="com.github.viclovsky.swagger.coverage.model.SwaggerCoverageResults" --> Swagger Coverage: CUSTOM_TEST_REPORT
<#list data.conditionStatisticsMap as key, value>
<#assign nameKey = "predicate.${key}.name"> <#assign descriptionKey = "predicate.${key}.description">

${i18[nameKey]!nameKey}

${i18[descriptionKey]!descriptionKey}
<#list value.coveredOperation as conditionItem>
${i18["details.condition.conditionname"]}e ${i18["details.condition.details"]}
 ${conditionItem.operation} ${conditionItem.condition.name} ${conditionItem.condition.reason?no_esc}
<#list value.uncoveredOperation as conditionItem>
${i18["details.condition.operation"]} ${i18["details.condition.conditionname"]}e ${i18["details.condition.details"]}
 ${conditionItem.operation} ${conditionItem.condition.name} ${conditionItem.condition.reason?no_esc}
================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/petstory.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders" }, { "name": "user", "description": "Operations about user", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } } ], "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": true, "type": "array", "items": { "type": "string", "enum": [ "available", "pending", "sold" ], "default": "available" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": true, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/petstory_no_tags.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": true, "type": "array", "items": { "type": "string", "enum": [ "available", "pending", "sold" ], "default": "available" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": true, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/petstory_operation_wo_tags.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": true, "type": "array", "items": { "type": "string", "enum": [ "available", "pending", "sold" ], "default": "available" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": true, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/petstory_with_x_example.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders" }, { "name": "user", "description": "Operations about user", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } } ], "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": true, "type": "array", "items": { "type": "string", "enum": [ "available", "pending", "sold" ], "default": "available" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": true, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi" }, { "name": "string_parameter", "in": "query", "description": "Parameter with string x-example", "required": false, "type": "string", "x-example": "example_string_value" }, { "name": "integer_parameter", "in": "query", "description": "Parameter with integer x-example", "required": false, "type": "integer", "x-example": 100 }, { "name": "boolean_parameter", "in": "query", "description": "Parameter with boolean x-example", "required": false, "type": "boolean", "x-example": true }, { "name": "enum_parameter", "in": "query", "description": "Parameter with enum x-example", "required": false, "type": "string", "x-example": "first_value", "enum": ["first_value", "second_value"] }, { "name": "array_parameter", "in": "query", "description": "Parameter with array x-example", "required": false, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi", "x-example": "array_value" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/petstory_without_parameters.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders" }, { "name": "user", "description": "Operations about user", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } } ], "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/empty_parameters.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/findByStatus" : { "get" : { "parameters" : [ ], "responses" : { "200" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/enum_param_1.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/findByStatus" : { "get" : { "parameters" : [ { "name" : "status", "in" : "query", "required" : true, "x-example" : "available" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/enum_param_2.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/findByStatus" : { "get" : { "parameters" : [ { "name" : "status", "in" : "query", "required" : true, "x-example" : "pending" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/ignore_header.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/{petId}": { "delete" : { "parameters" : [ { "name" : "petId", "in" : "path", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "404" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/match_pattern.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/user/victor": { "get" : { "parameters" : [ { "name" : "username", "in" : "path", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/missed_in_swagger.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/some/operation/missed/in/swagger" : { "get" : { "parameters" : [ { "name" : "petId", "in" : "path", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { }, "400" : { }, "404" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/missed_in_swagger_without_x_example.json ================================================ { "swagger": "2.0", "host": "localhost", "schemes": [ "http" ], "consumes": [ "application/json" ], "produces": [ "" ], "paths": { "/some/operation/missed/in/swagger/without/x/example": { "get": { "parameters": [ { "name": "petId", "in": "path", "required": true, "x-example": "p" }, { "name": "X-Request-ID", "in": "header", "required": false, "x-example": "h" }, { "name": "Accept", "in": "header", "required": false, "x-example": "*/*" }, { "name": "Content-Type", "in": "header", "required": false, "x-example": "application/x-www-form-urlencoded; charset=ISO-8859-1" }, { "in": "body", "name": "body", "required": false } ], "responses": { "200": {} } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/not_200.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/{petId}/uploadImage": { "post" : { "parameters" : [ { "name" : "petId", "in" : "path", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "404" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/one_coverage.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/{petId}" : { "get" : { "parameters" : [ { "name" : "petId", "in" : "path", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { }, "400" : { }, "404" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/one_parameter.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/user/{username}": { "put" : { "parameters" : [ { "name" : "username", "in" : "path", "required" : true, "x-example" : "u" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "500" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/one_partial_coverage.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/{petId}" : { "get" : { "parameters" : [ { "name" : "petId", "in" : "path", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/test_empty_operation.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/store/inventory" : { "get" : { "responses" : { "404" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v2/swagger-coverage-output/two_coverage.json ================================================ { "swagger" : "2.0", "host" : "localhost", "schemes" : [ "http" ], "consumes" : [ "application/json" ], "produces" : [ "" ], "paths" : { "/pet/findByStatus" : { "get" : { "parameters" : [ { "name": "tags", "in": "query", "required" : true, "x-example" : "p" }, { "name" : "X-Request-ID", "in" : "header", "required" : false, "x-example" : "h" }, { "name" : "Accept", "in" : "header", "required" : false, "x-example" : "*/*" }, { "name" : "Content-Type", "in" : "header", "required" : false, "x-example" : "application/x-www-form-urlencoded; charset=ISO-8859-1" } ], "responses" : { "200" : { } } } } } } ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/operations/pet_petId.yaml ================================================ get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/json: schema: $ref: "../petstory_ref_operations.yaml#/components/schemas/Pet" application/xml: schema: $ref: "../petstory_ref_operations.yaml#/components/schemas/Pet" "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Pet not found security: - petstore_auth: - write:pets - read:pets ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/petstory.yaml ================================================ openapi: 3.0.0 info: description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: 1.0.3 title: Swagger Petstore termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: http://swagger.io - name: store description: Access to Petstore orders - name: user description: Operations about user externalDocs: description: Find out more about our store url: http://swagger.io paths: "/pet/{petId}": get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Pet not found security: - petstore_auth: - write:pets - read:pets "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - write:pets - read:pets /pet: post: tags: - pet summary: Add a new pet to the store description: "" operationId: addPet requestBody: $ref: "#/components/requestBodies/Pet" responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets put: tags: - pet summary: Update an existing pet description: "" operationId: updatePet requestBody: $ref: "#/components/requestBodies/Pet" responses: "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true explode: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets deprecated: true /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] "/store/order/{orderId}": get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: "400": description: Invalid ID supplied "404": description: Order not found /store/order: post: tags: - store summary: Place an order for a pet description: "" operationId: placeOrder requestBody: content: application/json: schema: $ref: "#/components/schemas/Order" description: order placed for purchasing the pet required: true responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid Order "/user/{username}": get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/User" application/xml: schema: $ref: "#/components/schemas/User" "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Updated user object required: true responses: "400": description: Invalid user supplied "404": description: User not found delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: "200": description: successful operation headers: X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 content: application/json: schema: type: string application/xml: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser responses: default: description: successful operation /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Created user object required: true responses: default: description: successful operation /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithArrayInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithListInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: https://petstore.swagger.io/v2 - url: http://petstore.swagger.io/v2 components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: "#/components/schemas/User" description: List of user object required: true Pet: content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store required: true securitySchemes: api_key: type: apiKey name: api_key in: header petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore.swagger.io/oauth/authorize scopes: read:pets: read your pets write:pets: modify pets in your account schemas: Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: "#/components/schemas/Category" name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: "#/components/schemas/Tag" status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean xml: name: Order User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/petstory_no_tags.yaml ================================================ openapi: 3.0.0 info: description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: 1.0.3 title: Swagger Petstore termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html paths: "/pet/{petId}": get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Pet not found security: - petstore_auth: - write:pets - read:pets "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - write:pets - read:pets /pet: post: tags: - pet summary: Add a new pet to the store description: "" operationId: addPet requestBody: $ref: "#/components/requestBodies/Pet" responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets put: tags: - pet summary: Update an existing pet description: "" operationId: updatePet requestBody: $ref: "#/components/requestBodies/Pet" responses: "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true explode: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets deprecated: true /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] "/store/order/{orderId}": get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: "400": description: Invalid ID supplied "404": description: Order not found /store/order: post: tags: - store summary: Place an order for a pet description: "" operationId: placeOrder requestBody: content: application/json: schema: $ref: "#/components/schemas/Order" description: order placed for purchasing the pet required: true responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid Order "/user/{username}": get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/User" application/xml: schema: $ref: "#/components/schemas/User" "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Updated user object required: true responses: "400": description: Invalid user supplied "404": description: User not found delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: "200": description: successful operation headers: X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 content: application/json: schema: type: string application/xml: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser responses: default: description: successful operation /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Created user object required: true responses: default: description: successful operation /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithArrayInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithListInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: https://petstore.swagger.io/v2 - url: http://petstore.swagger.io/v2 components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: "#/components/schemas/User" description: List of user object required: true Pet: content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store required: true securitySchemes: api_key: type: apiKey name: api_key in: header petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore.swagger.io/oauth/authorize scopes: read:pets: read your pets write:pets: modify pets in your account schemas: Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: "#/components/schemas/Category" name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: "#/components/schemas/Tag" status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean xml: name: Order User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/petstory_operation_wo_tags.yaml ================================================ openapi: 3.0.0 info: description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: 1.0.3 title: Swagger Petstore termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html paths: "/pet/{petId}": get: summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Pet not found security: - petstore_auth: - write:pets - read:pets "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - write:pets - read:pets /pet: post: tags: - pet summary: Add a new pet to the store description: "" operationId: addPet requestBody: $ref: "#/components/requestBodies/Pet" responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets put: tags: - pet summary: Update an existing pet description: "" operationId: updatePet requestBody: $ref: "#/components/requestBodies/Pet" responses: "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true explode: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets deprecated: true /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] "/store/order/{orderId}": get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: "400": description: Invalid ID supplied "404": description: Order not found /store/order: post: tags: - store summary: Place an order for a pet description: "" operationId: placeOrder requestBody: content: application/json: schema: $ref: "#/components/schemas/Order" description: order placed for purchasing the pet required: true responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid Order "/user/{username}": get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/User" application/xml: schema: $ref: "#/components/schemas/User" "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Updated user object required: true responses: "400": description: Invalid user supplied "404": description: User not found delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: "200": description: successful operation headers: X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 content: application/json: schema: type: string application/xml: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser responses: default: description: successful operation /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Created user object required: true responses: default: description: successful operation /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithArrayInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithListInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: https://petstore.swagger.io/v2 - url: http://petstore.swagger.io/v2 components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: "#/components/schemas/User" description: List of user object required: true Pet: content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store required: true securitySchemes: api_key: type: apiKey name: api_key in: header petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore.swagger.io/oauth/authorize scopes: read:pets: read your pets write:pets: modify pets in your account schemas: Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: "#/components/schemas/Category" name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: "#/components/schemas/Tag" status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean xml: name: Order User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/petstory_ref_operations.yaml ================================================ openapi: 3.0.0 info: description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: 1.0.3 title: Swagger Petstore termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: http://swagger.io - name: store description: Access to Petstore orders - name: user description: Operations about user externalDocs: description: Find out more about our store url: http://swagger.io paths: "/pet/{petId}": $ref: operations/pet_petId.yaml "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - write:pets - read:pets /pet: post: tags: - pet summary: Add a new pet to the store description: "" operationId: addPet requestBody: $ref: "#/components/requestBodies/Pet" responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets put: tags: - pet summary: Update an existing pet description: "" operationId: updatePet requestBody: $ref: "#/components/requestBodies/Pet" responses: "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true explode: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets deprecated: true /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] "/store/order/{orderId}": get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: "400": description: Invalid ID supplied "404": description: Order not found /store/order: post: tags: - store summary: Place an order for a pet description: "" operationId: placeOrder requestBody: content: application/json: schema: $ref: "#/components/schemas/Order" description: order placed for purchasing the pet required: true responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid Order "/user/{username}": get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/User" application/xml: schema: $ref: "#/components/schemas/User" "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Updated user object required: true responses: "400": description: Invalid user supplied "404": description: User not found delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: "200": description: successful operation headers: X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 content: application/json: schema: type: string application/xml: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser responses: default: description: successful operation /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Created user object required: true responses: default: description: successful operation /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithArrayInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithListInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: https://petstore.swagger.io/v2 - url: http://petstore.swagger.io/v2 components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: "#/components/schemas/User" description: List of user object required: true Pet: content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store required: true securitySchemes: api_key: type: apiKey name: api_key in: header petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore.swagger.io/oauth/authorize scopes: read:pets: read your pets write:pets: modify pets in your account schemas: Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: "#/components/schemas/Category" name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: "#/components/schemas/Tag" status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean xml: name: Order User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/petstory_with_x_example.yaml ================================================ openapi: 3.0.0 info: description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: 1.0.3 title: Swagger Petstore termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: http://swagger.io - name: store description: Access to Petstore orders - name: user description: Operations about user externalDocs: description: Find out more about our store url: http://swagger.io paths: "/pet/{petId}": get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Pet not found security: - petstore_auth: - write:pets - read:pets "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - write:pets - read:pets /pet: post: tags: - pet summary: Add a new pet to the store description: "" operationId: addPet requestBody: $ref: "#/components/requestBodies/Pet" responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets put: tags: - pet summary: Update an existing pet description: "" operationId: updatePet requestBody: $ref: "#/components/requestBodies/Pet" responses: "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true explode: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true explode: true schema: type: array items: type: string - name: string_parameter in: query description: Parameter with string x-example required: false example: example_string_value schema: type: string - name: integer_parameter in: query description: Parameter with integer x-example required: false example: 100 schema: type: integer - name: boolean_parameter in: query description: Parameter with boolean x-example required: false example: true schema: type: boolean - name: enum_parameter in: query description: Parameter with enum x-example required: false example: first_value schema: type: string enum: - first_value - second_value - name: array_parameter in: query description: Parameter with array x-example required: false example: array_value explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets deprecated: true /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] "/store/order/{orderId}": get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: "400": description: Invalid ID supplied "404": description: Order not found /store/order: post: tags: - store summary: Place an order for a pet description: "" operationId: placeOrder requestBody: content: application/json: schema: $ref: "#/components/schemas/Order" description: order placed for purchasing the pet required: true responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid Order "/user/{username}": get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/User" application/xml: schema: $ref: "#/components/schemas/User" "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Updated user object required: true responses: "400": description: Invalid user supplied "404": description: User not found delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: "200": description: successful operation headers: X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 content: application/json: schema: type: string application/xml: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser responses: default: description: successful operation /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Created user object required: true responses: default: description: successful operation /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithArrayInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithListInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: https://petstore.swagger.io/v2 - url: http://petstore.swagger.io/v2 components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: "#/components/schemas/User" description: List of user object required: true Pet: content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store required: true securitySchemes: api_key: type: apiKey name: api_key in: header petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore.swagger.io/oauth/authorize scopes: read:pets: read your pets write:pets: modify pets in your account schemas: Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: "#/components/schemas/Category" name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: "#/components/schemas/Tag" status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean xml: name: Order User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/petstory_without_parameters.yaml ================================================ openapi: 3.0.0 info: description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." version: 1.0.3 title: Swagger Petstore termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: http://swagger.io - name: store description: Access to Petstore orders - name: user description: Operations about user externalDocs: description: Find out more about our store url: http://swagger.io paths: "/pet/{petId}": get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Pet not found security: - petstore_auth: - write:pets - read:pets "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - write:pets - read:pets /pet: post: tags: - pet summary: Add a new pet to the store description: "" operationId: addPet requestBody: $ref: "#/components/requestBodies/Pet" responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets put: tags: - pet summary: Update an existing pet description: "" operationId: updatePet requestBody: $ref: "#/components/requestBodies/Pet" responses: "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: "#/components/schemas/Pet" application/xml: schema: type: array items: $ref: "#/components/schemas/Pet" "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets deprecated: true /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] "/store/order/{orderId}": get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 10 responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 minimum: 1 responses: "400": description: Invalid ID supplied "404": description: Order not found /store/order: post: tags: - store summary: Place an order for a pet description: "" operationId: placeOrder requestBody: content: application/json: schema: $ref: "#/components/schemas/Order" description: order placed for purchasing the pet required: true responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/Order" application/xml: schema: $ref: "#/components/schemas/Order" "400": description: Invalid Order "/user/{username}": get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: $ref: "#/components/schemas/User" application/xml: schema: $ref: "#/components/schemas/User" "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be updated required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Updated user object required: true responses: "400": description: Invalid user supplied "404": description: User not found delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string - name: password in: query description: The password for login in clear text required: true schema: type: string responses: "200": description: successful operation headers: X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 content: application/json: schema: type: string application/xml: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser responses: default: description: successful operation /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: content: application/json: schema: $ref: "#/components/schemas/User" description: Created user object required: true responses: default: description: successful operation /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithArrayInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: "" operationId: createUsersWithListInput requestBody: $ref: "#/components/requestBodies/UserArray" responses: default: description: successful operation externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: https://petstore.swagger.io/v2 - url: http://petstore.swagger.io/v2 components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: "#/components/schemas/User" description: List of user object required: true Pet: content: application/json: schema: $ref: "#/components/schemas/Pet" application/xml: schema: $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store required: true securitySchemes: api_key: type: apiKey name: api_key in: header petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore.swagger.io/oauth/authorize scopes: read:pets: read your pets write:pets: modify pets in your account schemas: Category: type: object properties: id: type: integer format: int64 name: type: string xml: name: Category Pet: type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: "#/components/schemas/Category" name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: "#/components/schemas/Tag" status: type: string description: pet status in the store enum: - available - pending - sold xml: name: Pet Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string Order: type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean xml: name: Order User: type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/empty_parameters.yaml ================================================ --- openapi: 3.0.0 paths: /pet/findByStatus: get: responses: "200": description: "" "400": description: "" "404": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/enum_param_1.yaml ================================================ --- openapi: 3.0.0 paths: /pet/findByStatus: get: parameters: - name: status in: query required: true example: available schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/enum_param_2.yaml ================================================ --- openapi: 3.0.0 paths: /pet/findByStatus: get: parameters: - name: status in: query required: true example: pending schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/ignore_header.yaml ================================================ --- openapi: 3.0.0 paths: "/pet/{petId}": delete: parameters: - name: petId in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "404": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/match_pattern.yaml ================================================ --- openapi: 3.0.0 paths: /user/victor: get: parameters: - name: username in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/missed_in_swagger.yaml ================================================ --- openapi: 3.0.0 paths: /some/operation/missed/in/swagger: get: parameters: - name: petId in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" "400": description: "" "404": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/missed_in_swagger_without_x_example.yaml ================================================ --- openapi: 3.0.0 paths: /some/operation/missed/in/swagger/without/x/example: get: parameters: - name: petId in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string requestBody: content: application/json: schema: {} responses: "200": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/not_200.yaml ================================================ --- openapi: 3.0.0 paths: "/pet/{petId}/uploadImage": post: parameters: - name: petId in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "404": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/one_coverage.yaml ================================================ --- openapi: 3.0.0 paths: "/pet/{petId}": get: parameters: - name: petId in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" "400": description: "" "404": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/one_parameter.yaml ================================================ --- openapi: 3.0.0 paths: "/user/{username}": put: parameters: - name: username in: path required: true example: u schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "500": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/one_partial_coverage.yaml ================================================ --- openapi: 3.0.0 paths: "/pet/{petId}": get: parameters: - name: petId in: path required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/test_empty_operation.yaml ================================================ --- openapi: 3.0.0 paths: /store/inventory: get: responses: "404": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commandline/src/test/resources/v3/swagger-coverage-output/two_coverage.yaml ================================================ --- openapi: 3.0.0 paths: /pet/findByStatus: get: parameters: - name: tags in: query required: true example: p schema: type: string - name: X-Request-ID in: header required: false example: h schema: type: string - name: Accept in: header required: false example: "*/*" schema: type: string - name: Content-Type in: header required: false example: application/x-www-form-urlencoded; charset=ISO-8859-1 schema: type: string responses: "200": description: "" servers: - url: http://localhost info: version: "" title: "" ================================================ FILE: swagger-coverage-commons/build.gradle.kts ================================================ plugins { java `java-library` } description = "Swagger Coverage Commons" repositories { mavenCentral() } dependencies { implementation("org.freemarker:freemarker") implementation("io.swagger:swagger-models") //dirty fix https://github.com/viclovsky/swagger-coverage/issues/90 implementation("io.swagger.core.v3:swagger-core") implementation("io.swagger.core.v3:swagger-models") implementation("org.slf4j:slf4j-api") implementation("com.fasterxml.jackson.core:jackson-core") implementation("com.fasterxml.jackson.core:jackson-databind") implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.10.1") testImplementation("junit:junit") } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/CoverageOutputReader.java ================================================ package com.github.viclovsky.swagger.coverage; import java.nio.file.Path; import java.util.Set; public interface CoverageOutputReader { Set getOutputs(); } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/CoverageOutputWriter.java ================================================ package com.github.viclovsky.swagger.coverage; import io.swagger.models.Swagger; import io.swagger.v3.oas.models.OpenAPI; public interface CoverageOutputWriter { void write(Swagger swagger); void write(OpenAPI openAPI); } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/FileSystemOutputReader.java ================================================ package com.github.viclovsky.swagger.coverage; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; import java.util.stream.Stream; public class FileSystemOutputReader implements CoverageOutputReader { private final Path outputDirectory; public FileSystemOutputReader(final Path outputDirectory) { this.outputDirectory = outputDirectory; } @Override public Set getOutputs() { Set outputs = new HashSet<>(); try (Stream paths = Files.walk(outputDirectory)) { paths.filter(Files::isRegularFile).forEach(outputs::add); } catch (IOException e) { throw new SwaggerCoverageReadException("can't read coverage file's", e); } return outputs; } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/FileSystemOutputWriter.java ================================================ package com.github.viclovsky.swagger.coverage; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.viclovsky.swagger.coverage.model.SwaggerCoverage2ModelJackson; import io.swagger.models.Swagger; import io.swagger.v3.oas.models.OpenAPI; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageUtils.generateJsonCoverageOutputName; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageUtils.generateYamlCoverageOutputName; import static java.nio.file.StandardOpenOption.CREATE_NEW; public class FileSystemOutputWriter implements CoverageOutputWriter { private final Path outputDirectory; private final ObjectMapper jsonMapper; private final ObjectMapper yamlMapper; public FileSystemOutputWriter(final Path outputDirectory) { this.outputDirectory = outputDirectory; this.jsonMapper = SwaggerCoverage2ModelJackson.createJsonMapper(); this.yamlMapper = SwaggerCoverage2ModelJackson.createYamlMapper(); } private void createDirectories(final Path directory) { try { Files.createDirectories(directory); } catch (IOException e) { throw new SwaggerCoverageWriteException("Could not create Swagger output directory", e); } } @Override public void write(Swagger swagger) { final String swaggerResultName = generateJsonCoverageOutputName(); createDirectories(outputDirectory); Path file = outputDirectory.resolve(swaggerResultName); try (OutputStream os = Files.newOutputStream(file, CREATE_NEW)) { jsonMapper.writerWithDefaultPrettyPrinter().writeValue(os, swagger); } catch (IOException e) { throw new SwaggerCoverageWriteException("Could not write Swagger", e); } } @Override public void write(OpenAPI openAPI) { final String swaggerResultName = generateYamlCoverageOutputName(); createDirectories(outputDirectory); Path file = outputDirectory.resolve(swaggerResultName); try (OutputStream os = Files.newOutputStream(file, CREATE_NEW)) { yamlMapper.writerWithDefaultPrettyPrinter().writeValue(os, openAPI); } catch (IOException e) { throw new SwaggerCoverageWriteException("Could not write Swagger", e); } } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/SwaggerCoverageConstants.java ================================================ package com.github.viclovsky.swagger.coverage; public final class SwaggerCoverageConstants { public static final String BODY_PARAM_NAME = "body"; public static final String COVERAGE_HTML_REPORT_NAME = "swagger-coverage-report.html"; public static final String COVERAGE_RESULTS_NAME = "swagger-coverage-results.json"; public static final String OUTPUT_DIRECTORY = "swagger-coverage-output"; public static final String COVERAGE_JSON_OUTPUT_FILE_SUFFIX = "-coverage.json"; public static final String COVERAGE_YAML_OUTPUT_FILE_SUFFIX = "-coverage.yaml"; private SwaggerCoverageConstants() { throw new IllegalStateException("Do not instance"); } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/SwaggerCoverageReadException.java ================================================ package com.github.viclovsky.swagger.coverage; class SwaggerCoverageReadException extends RuntimeException { SwaggerCoverageReadException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/SwaggerCoverageUtils.java ================================================ package com.github.viclovsky.swagger.coverage; import java.util.UUID; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.COVERAGE_JSON_OUTPUT_FILE_SUFFIX; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.COVERAGE_YAML_OUTPUT_FILE_SUFFIX; public final class SwaggerCoverageUtils { SwaggerCoverageUtils() { throw new IllegalStateException("Do not instance"); } public static String generateJsonCoverageOutputName() { return generateCoverageOutputName(UUID.randomUUID().toString(), COVERAGE_JSON_OUTPUT_FILE_SUFFIX); } public static String generateYamlCoverageOutputName() { return generateCoverageOutputName(UUID.randomUUID().toString(), COVERAGE_YAML_OUTPUT_FILE_SUFFIX); } private static String generateCoverageOutputName(String uuid, String suffix) { return uuid + suffix; } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/SwaggerCoverageWriteException.java ================================================ package com.github.viclovsky.swagger.coverage; public class SwaggerCoverageWriteException extends RuntimeException { public SwaggerCoverageWriteException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/model/SwaggerCoverage2ModelJackson.java ================================================ package com.github.viclovsky.swagger.coverage.model; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; import static com.fasterxml.jackson.databind.MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME; @SuppressWarnings("PMD.ClassNamingConventions") public final class SwaggerCoverage2ModelJackson { private SwaggerCoverage2ModelJackson() { throw new IllegalStateException("Do not instance SwaggerCoverage2ModelJackson"); } public static ObjectMapper createJsonMapper() { return new ObjectMapper() .configure(USE_WRAPPER_NAME_AS_PROPERTY_NAME, true) .setSerializationInclusion(NON_NULL); } public static ObjectMapper createYamlMapper() { return new ObjectMapper(new YAMLFactory()) .configure(USE_WRAPPER_NAME_AS_PROPERTY_NAME, true) .setSerializationInclusion(NON_NULL); } } ================================================ FILE: swagger-coverage-commons/src/main/java/com/github/viclovsky/swagger/coverage/utils/FreemarkerUtils.java ================================================ package com.github.viclovsky.swagger.coverage.utils; import freemarker.cache.FileTemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static java.nio.charset.StandardCharsets.UTF_8; /** * @author eroshenkoam (Artem Eroshenko). */ public final class FreemarkerUtils { private static final Logger log = LoggerFactory.getLogger(FreemarkerUtils.class); private final static String TEMPLATES = "templates"; private FreemarkerUtils() { } public static String processTemplate(final String path, final Object object) { return processTemplate(path,"en", "0.###", object); } private static String proccessTemplate(Configuration configuration, String locale, String templateName, final Object object) { Map messages = readMessages(locale); try { final Map data = new HashMap<>(); data.put("data", object); data.put("messages", messages); final Writer writer = new StringWriter(); final Template template = configuration.getTemplate(templateName); template.process(data, writer); return writer.toString(); } catch (IOException | TemplateException e) { throw new RuntimeException(e); } } public static String processTemplate(final String path, String locale, String numberFormat, final Object object) { final Configuration configuration = new Configuration(Configuration.VERSION_2_3_28); configuration.setClassForTemplateLoading(FreemarkerUtils.class, "/"); configuration.setDefaultEncoding("UTF-8"); configuration.setNumberFormat(numberFormat); return proccessTemplate(configuration, locale, path, object); } public static String processCustomTemplate(final String customTemplatePath, String locale, String numberFormat, Object object) throws IOException { File template = new File(customTemplatePath); final Configuration configuration = new Configuration(Configuration.VERSION_2_3_28); configuration.setTemplateLoader(new FileTemplateLoader(template.getParentFile())); configuration.setDefaultEncoding("UTF-8"); configuration.setNumberFormat(numberFormat); return proccessTemplate(configuration, locale, template.getName(), object); } public static Map readMessages(String localeCode){ Properties properties = new Properties(); HashMap mymap= new HashMap(); String resourceName = "message." + localeCode; // could also be a constant log.info("read locale from " + resourceName); ClassLoader loader = Thread.currentThread().getContextClassLoader(); try(InputStream resourceStream = loader.getResourceAsStream(resourceName)) { InputStreamReader isr = new InputStreamReader(resourceStream, UTF_8); properties.load(isr); } catch (Exception e) { log.error("can't read locale resource" + e); } for (String key : properties.stringPropertyNames()) { String value = properties.getProperty(key); mymap.put(key, value); } return mymap; } } ================================================ FILE: swagger-coverage-commons/src/main/resources/details/condition.ftl ================================================ <#import "operation.ftl" as operation /> <#macro list coverage prefix>
<#list coverage as key> <@operation.details name=key operationResult=operationMap[key] target=prefix + "-" + key?counter />
================================================ FILE: swagger-coverage-commons/src/main/resources/details/operation.ftl ================================================ <#import "../ui.ftl" as ui/> <#macro list coverage prefix>
<#list coverage as key, value>
${key}
${i18["details.operation.status"]}: ${value.getResponses()?keys?join(",")}
${i18["details.operation.parameters"]}
<#if value.getParameters()??> <#list value.getParameters() as p> <#if p.getExtensions()??> <#if p.getExtensions()["x-example"]??> <#if p.getExtensions()["x-example"]?is_boolean> <#else> <#elseif p.getExample()??> <#if p.getExample()?is_boolean> <#else>
${i18["details.operation.parameter.type"]} ${i18["details.operation.parameter.name"]} ${i18["details.operation.parameter.value"]}
${p.getName()}${p.getExtensions()["x-example"]?c}${p.getExtensions()["x-example"]} ${p.getExample()?c}${p.getExample()}
<#if coverage?size == 0> ${i18["details.operation.no_data"]}
<#macro details name operationResult target>
<@ui.coverageStateBadget operationResult=operationResult />
${operationResult.operationKey.path} <#-- ${operationResult.description}-->
${operationResult.processCount} ${i18["details.operation.calls"]}
<@ui.progress full=operationResult.allConditionCount current=operationResult.coveredConditionCount postfix=i18["details.conditionprogress.postfix"] />
<@conditionList list=operationResult.conditions />
<#macro conditionList list>
<#list list as condition> <#assign trStyle = "table-danger"> <#if condition.covered> <#assign trStyle = "table-success">
${i18["details.conditionlist.name"]} ${i18["details.conditionlist.details"]}
<#if condition.covered> <#else>  ${condition.name} ${condition.reason}
================================================ FILE: swagger-coverage-commons/src/main/resources/details/tag.ftl ================================================ <#import "operation.ftl" as operation /> <#import "../ui.ftl" as ui/> <#macro list tags>
<#list tags as tag, tagCoverage>
${tag}<#if tagCoverage.tag.getDescription()??>: ${tagCoverage.tag.getDescription()}
${tagCoverage.operations?size} ${i18["details.tag.operations"]}
${tagCoverage.callCounts} ${i18["details.operation.calls"]}
<@ui.progress full=tagCoverage.conditionCounter.all current=tagCoverage.conditionCounter.covered postfix=i18["details.conditionprogress.postfix"] />
<@ui.coverageBadget counter=tagCoverage.coverageCounter/> <#list tagCoverage.operations as op> <@operation.details name=op operationResult=operationMap[op] target="tag-" + tag + "-" + op?counter />
================================================ FILE: swagger-coverage-commons/src/main/resources/message.en ================================================ menu.summary=Summary menu.operations=Operations details menu.tags=Tags details menu.condition=Conditions details menu.generation=Generation info common.state.full=Full common.state.partial=Partial common.state.empty=Empty common.state.no_call=No call common.state.deprecated=Deprecated operations.all=All operations.full=Full operations.partial=Partial operations.empty=Empty operations.no_call=No call operations.missed=Missed operations.deprecated=Deprecated summary.operations=Operations coverage summary summary.operations.all=All operations summary.operations.no_call=Operations without calls summary.operations.missed=Missed request summary.conditions=Conditions coverage summary summary.conditions.total=Total summary.conditions.covered=Covered summary.conditions.uncovered=Uncovered summary.tags=Tags coverage summary summary.tags.all=All tags summary.tags.no_call=Tags without calls badged.full=Full coverage badged.partial=Partial coverage badged.empty=Empty coverage details.operation.calls=calls details.operation.no_data=No data details.operation.status=Response status details.operation.parameters=Parameters details.operation.parameter.type=Type details.operation.parameter.name=Name details.operation.parameter.value=Value details.conditionlist.name=Condition name details.conditionlist.details=Details details.conditionprogress.postfix=condition covered details.tag.operations=operations details.condition.operation=Operation name details.condition.conditionname=Condition name details.condition.details=Details generation.configuration=Configuration generation.parsed_file_count=Parsed result files generation.time=Generation time generation.result_file_created_interval=Result file create interval generation.report.date=Generation report date predicate.ParameterValueConditionPredicate.name=All parameter values predicate.ParameterValueConditionPredicate.description=Verification that the method is called with all parameters described in the enum predicate.NotOnlyParameterListValueConditionPredicate.name=Values not only from the list predicate.NotOnlyParameterListValueConditionPredicate.description = Verify that the method was called with a value that is not described in the parameter enum predicate.DefaultBodyConditionPredicate.name = Request Body Availability predicate.DefaultBodyConditionPredicate.description = Verify that the request body was not empty predicate.DefaultStatusConditionPredicate.name = Response Status predicate.DefaultStatusConditionPredicate.description = Check that there is an answer with the http status described in responses predicate.DefaultParameterConditionPredicate.name = Check parameter value predicate.DefaultParameterConditionPredicate.description = Check that the parameter is empty or not empty predicate.FullStatusConditionPredicate.name = Only the described statuses predicate.FullStatusConditionPredicate.description = Check that no status was received that was not described predicate.DefaultPropertyConditionPredicate.name = Check property value predicate.DefaultPropertyConditionPredicate.description = Check that the property is empty or not empty predicate.PropertyValueConditionPredicate.name = All property values predicate.PropertyValueConditionPredicate.description = Verification that the method is called with all properties described in the enum predicate.PropertyValueNotOnlyConditionPredicate.name = Values not only from the list predicate.PropertyValueNotOnlyConditionPredicate.description = Verify that the method was called with a value that is not described in the property enum ================================================ FILE: swagger-coverage-commons/src/main/resources/message.ru ================================================ menu.summary=Сводка menu.operations=Методы menu.tags=Группы menu.condition=Варианты menu.generation=Об отчёте common.state.full=Полностью common.state.partial=Частично common.state.empty=Не покрыто common.state.no_call=Пропущено common.state.deprecated=Не используется operations.all=Все operations.full=Полностью operations.partial=Частично operations.empty=Не покрыто operations.no_call=Пропущено operations.missed=Лишние запросы operations.deprecated=Не используются summary.operations=Сводка по методам summary.operations.all=Всего summary.operations.no_call=Пропущено summary.operations.missed=Лишние запросы summary.conditions=Сводка по вариантам summary.conditions.total=Всего summary.conditions.covered=Покрыто summary.conditions.uncovered=Не покрыто summary.tags=Сводка по группам summary.tags.all=Всего summary.tags.no_call=Пропущено badged.full=Полностью покрыто badged.partial=Частично покрыто badged.empty=Не покрыто details.operation.calls=вызов(ов) details.operation.no_data=Нет данных details.operation.status=Статус ответа details.operation.parameters=Параметры details.operation.parameter.type=Тип details.operation.parameter.name=Название details.operation.parameter.value=Значения details.conditionlist.name=Вариант details.conditionlist.details=Подробности details.conditionprogress.postfix=вариантов покрыто details.tag.operations=методов details.condition.operation=Метод details.condition.conditionname=Вариант details.condition.details=Подробности generation.configuration=Настройки generation.parsed_file_count=Проверено файлов с результатами generation.time=Затраченое на отчёт время generation.result_file_created_interval=Файлы с результатами созданы generation.report.date=Дата генерации отчёта predicate.ParameterValueConditionPredicate.name=Все значения параметра predicate.ParameterValueConditionPredicate.description=Проверка, что метод вызван со всеми описанным в enum параметра predicate.NotOnlyParameterListValueConditionPredicate.name=Значения не только из списка predicate.NotOnlyParameterListValueConditionPredicate.description=Проверка, что метод вызывался со значением, которое не описано в enum параметра predicate.DefaultBodyConditionPredicate.name=Наличие тела запроса predicate.DefaultBodyConditionPredicate.description=Проверка, что тело запроса было не пустым predicate.DefaultStatusConditionPredicate.name=Статус ответа predicate.DefaultStatusConditionPredicate.description=Проверка, что есть ответ с описанным в responses http-статусом predicate.DefaultParameterConditionPredicate.name=Проверка значения параметра predicate.DefaultParameterConditionPredicate.description=Проверка, что параметр пустой или не пустой predicate.FullStatusConditionPredicate.name=Только описанные статусы predicate.FullStatusConditionPredicate.description=Проверка, что не было получено статусов, которые не описаны predicate.DefaultPropertyConditionPredicate.name=Проверка значения проперти predicate.DefaultPropertyConditionPredicate.description=Проверка, что проперти пустая или не пустая predicate.PropertyValueConditionPredicate.name=Все значения проперти predicate.PropertyValueConditionPredicate.description=Проверка, что метод вызван со всеми описанным в enum проперти predicate.PropertyValueNotOnlyConditionPredicate.name=Значения не только из списка predicate.PropertyValueNotOnlyConditionPredicate.description=Проверка, что метод вызывался со значением, которое не описано в enum проперти ================================================ FILE: swagger-coverage-commons/src/main/resources/report.ftl ================================================ <#ftl output_format="HTML"> <#global i18=messages> <#global operationMap=data.flatOperations> <#-- @ftlvariable ftlvariable name="data" type="com.github.viclovsky.swagger.coverage.model.SwaggerCoverageResults" --> <#import "ui.ftl" as ui/> <#import "sections/summary.ftl" as summary /> <#import "sections/generation.ftl" as generation /> <#import "details/operation.ftl" as operations /> <#import "details/condition.ftl" as condition /> <#import "details/tag.ftl" as tag /> Swagger Coverage

${i18["menu.summary"]}

<@summary.operations operationCoveredMap=data.coverageOperationMap /> <@summary.calls data=data /> <@summary.tags tagsDetail=data.tagCoverageMap tagCounter=data.tagCounter /> <@summary.conditions counter=data.conditionCounter />

${i18["menu.operations"]}


<@condition.list coverage=data.coverageOperationMap.full + data.coverageOperationMap.party + data.coverageOperationMap.empty prefix="condition"/>
<@condition.list coverage=data.coverageOperationMap.full prefix="full"/>
<@condition.list coverage=data.coverageOperationMap.party prefix="party"/>
<@condition.list coverage=data.coverageOperationMap.empty prefix="empty"/>
<@condition.list coverage=data.zeroCall prefix="zero"/>
<@operations.list coverage=data.missed prefix="missed"/>
<@operations.list coverage=data.deprecated prefix="deprecated"/>

${i18["menu.tags"]}

<@tag.list tags=data.tagCoverageMap/>

${i18["menu.condition"]}

<#list data.conditionStatisticsMap as key, value>
<#assign nameKey = "predicate.${key}.name"> <#assign descriptionKey = "predicate.${key}.description">

${i18[nameKey]!nameKey}

${i18[descriptionKey]!descriptionKey}
<@ui.progress full=value.allCount current=value.coveredCount postfix=i18["details.conditionprogress.postfix"] />

<#list value.coveredOperation as conditionItem>
${i18["details.condition.operation"]} ${i18["details.condition.conditionname"]}e ${i18["details.condition.details"]}
 ${conditionItem.operation} ${conditionItem.condition.name} ${conditionItem.condition.reason?no_esc}
<#list value.uncoveredOperation as conditionItem>
${i18["details.condition.operation"]} ${i18["details.condition.conditionname"]}e ${i18["details.condition.details"]}
 ${conditionItem.operation} ${conditionItem.condition.name} ${conditionItem.condition.reason?no_esc}

${i18["menu.generation"]}

<@generation.data statistic=data.generationStatistics/>

${i18["generation.configuration"]}

================================================ FILE: swagger-coverage-commons/src/main/resources/sections/generation.ftl ================================================ <#macro data statistic>
${i18["generation.parsed_file_count"]}: ${statistic.resultFileCount}
${i18["generation.time"]}: ${statistic.generationTime} ms
${i18["generation.result_file_created_interval"]}: ${statistic.fileResultDateInterval}
${i18["generation.report.date"]}: ${statistic.generateDate}
================================================ FILE: swagger-coverage-commons/src/main/resources/sections/summary.ftl ================================================ <#import "../ui.ftl" as ui/> <#macro operations operationCoveredMap>

${i18["summary.operations"]}

<@ui.coverageBadget counter=operationCoveredMap.counter/> <#macro calls data>
<#macro conditions counter>

${i18["summary.conditions"]}

<#if counter.all gt 0 > <@ui.progressbar current = counter.covered full = counter.all height=50/>
<#macro tags tagsDetail tagCounter>

${i18["summary.tags"]}

<@ui.coverageBadget counter=tagCounter/>
================================================ FILE: swagger-coverage-commons/src/main/resources/ui.ftl ================================================ <#macro progressbar current full height=16> <#if full gt 0 > <#assign percentValue = 100*current/(full)> <#assign bgStyle = "bg-danger"> <#if percentValue gt 33> <#assign bgStyle = "bg-warning"> <#if percentValue gt 66> <#assign bgStyle = "bg-success">
<#macro progress current full postfix>
<#if full gt 0 > ${current}/${full} (${100*(current/full)}%) <#else> -- ${postfix}
<#if full gt 0 > <@progressbar current = current full = full />
<#macro coverageBadget counter> <#if counter.all gt 0> <#assign fullValue = counter.full * 100 / counter.all> <#assign partlyValue = counter.party * 100 / counter.all> <#assign emptyValue = counter.empty * 100 / counter.all>
<#macro coverageStateBadget operationResult> <#if operationResult.processCount == 0> ${i18["common.state.no_call"]} <#else> <#switch operationResult.state> <#case "FULL"> ${i18["common.state.full"]} <#break> <#case "PARTY"> ${i18["common.state.partial"]} <#break> <#case "EMPTY"> ${i18["common.state.empty"]} <#break> <#default> <#macro success text> ${text} <#macro danger text> ${text} <#macro question text> ${text} ================================================ FILE: swagger-coverage-karate/README.md ================================================ # swagger-coverage for Karate This module provides an integration of swagger-coverage for the [Karate Framework](https://github.com/karatelabs/karate). With the help of a single test runner, the coverage report can thus be generated automatically after the API tests. ## How it Works Swagger-coverage for Karate uses the [server-side features](https://github.com/karatelabs/karate/tree/master/karate-netty#karate-netty) of the Karate framework to implement a proxy server which intercepts the HTTP calls, extracts the necessary information, and then redirects the call to the original destination. For this, a local mock server is started on a free port before running the tests to host the proxy. The port number is chosen at runtime and can be accessed by calling `karate.properties['proxy.port']`. This approach has the advantage that the tests do not have to be changed and swagger-coverage can also be integrated into existing Karate projects with minimal effort. In addition, multiple Test Runners can be used in parallel. Since you usually don't want to create a report every time you run the tests (e.g. during active test implementation), you can decide individually when to use the Coverage Test Runner. ## Setup and How to Use The Swagger Coverage Runner requires at least [Java](https://www.oracle.com/java/technologies/downloads/) 8 and the use of either JUnit 4 or JUnit 5. The `swagger-coverage-karate` artifact also includes the `swagger-coverage-commons` and the `swagger-coverage-commandline` modules. To add the dependency to your project when using Maven, add the following to your pom.xml: ```xml com.github.viclovsky swagger-coverage-karate ${latest-swagger-coverage-version} ``` If you use Gradle instead, add this to your build.gradle file: ```gradle testImplementation "com.github.viclovsky:swagger-coverage-karate:${latest-swagger-coverage-version}" ``` ### Coverage Directory and Naming Conventions The easiest way to provide the Test Runner with all the necessary information is to have a folder in the project where the required files are located. If these are named correctly, as in the following, it is only necessary to specify the path to this directory. > The files can also be specifically set in the [options](#options). If not, the coverage directory is always checked for the files seen below. ``` api-test-coverage (can be any path and name) | +-- swagger-coverage-config.json +-- swagger-specification.json/yaml ``` If specified, the directory will function as the working directory for the swagger-coverage tool. Therefore, not only the `swagger-coverage-output` folder is then created in there, where the generated swagger models of the HTTP calls are stored, but also the final HTML coverage report. If not specified, the needed file paths must specifically be provided in the arguments of the Test Runner and everything else will be generated within the current working directory (the root of the project, most of the time). ### The SwaggerCoverageRunner The `SwaggerCoverageRunner` extends the [Karate Runner](https://github.com/karatelabs/karate#junit-4-parallel-execution) class. Therefore, all options which are available there, can also be used with the coverage runner. This enables an easy migration, since it is only neccesary to add the additional builder methods to provide the runner with the needed information for creating the coverage report. For example: ```java import java.net.URI; import com.github.viclovsky.swagger.coverage.karate.SwaggerCoverageRunner; import com.intuit.karate.Results; import org.junit.jupiter.api.Test; public class CoverageReportRunner { @Test void testAll(){ Results results = SwaggerCoverageRunner.path("classpath:some/path") .coverageDir("api-test-coverage") .swaggerSpec(URI.create("https://petstore3.swagger.io/api/v3/openapi.json")) .oas3() .outputJunitXml(true) .karateEnv("dev") .parallel(1); } } ``` #### Available Options | Option | Description | |--------|--------------| | `.coverageDir(String)` | set the working directory for the swagger-coverage tool. See [Coverage Directory](#coverageDir). | | `.swaggerSpec(URI)` | specifically set the path to the Swagger/OpenAPI specification. Can also be a URL to a remote spec, useful for when swagger is hosted on the test environment.| | `.swaggerCoverageConfig(String)` | specifically set the path to the [Config File](https://github.com/viclovsky/swagger-coverage#configuration-options). | | `.swagger()` | use this when the specification uses the [Swagger/OpenAPI 2.0](https://swagger.io/specification/v2/) format. | | `.oas3()` | use this when the specification uses the [OpenAPI 3.0](https://swagger.io/specification/) format. | | `.backupCoverageOutput(boolean)` | backup the `swagger-coverage-output` folder, if one exists from a previous run. Default is set to `false`. | ### Swagger Coverage Options There are a few options you may want to decide on more flexible instead of setting them once at the start of the tests. For that reason, the `scOptions` object is provided and can be used anywhere in the Karate-context. It holds the following functionalities: #### `scOptions.setDestUrl(arg)` This tells the proxy server where to send the HTTP calls to. Most of the time, it is sufficient to set this once in the `karate-config.js`. But if needed, it can be changed before every single call. > Note that if your destination URL uses HTTPS, take a look at [The Problem with HTTPS and the Workaround](#https). #### `scOptions.setPathPattern(arg)` One of the conditions in the coverage report checks, whether a specified param was set in the test. In order for this to happen, the parameter name must be included in the swagger representation of the HTTP call. But unlike query parameters, for example, path parameters in Karate are not specified by their name but only by their value. That's why on the server side you can only get to this parameter name in a certain way. For this the path pattern in the style of `/some/path/{paramName}` is needed. To correctly use the aforementioned condition, this option can be used to specify the needed path pattern. For example: ```gherkin Feature: using the 'setPathPattern' option Background: * url "http://petstore3.swagger.io" * eval scOptions.setDestUrl("https://petstore3.swagger.io") Scenario: * eval scOptions.setPathPattern("/api/v3/pet/{petId}") Given path "api", "v3", "pet", 2 When method GET Then status 200 ``` #### `scOptions.ignoreNextCall()` Sometimes you don't want a call to be included in the coverage report, for example when reusing feature files to create entities etc. This options tells the proxy server to simply send the call to the destination URL without extracting any information and thus ignoring it for the coverage report. ## The Problem with HTTPS and the Workaround As of now, the karate proxy server does not support HTTPS calls, as it is unable to unpack them (more information can be found in this [thread](https://github.com/karatelabs/karate/issues/640)). Therefore, the requests must be send as HTTP calls when using the [SwaggerCoverageRunner](#runner), and then the proxy can send them to the HTTPS destination. The following example shows, how a dynamic setup can be achieved in the `karate-config.json`: ```javaScript function fn(){ var env = karate.env; karate.log('karate.env system property was:', env); if (!env){ env = 'dev'; } var uri = "://petstore3.swagger.io"; var protocol = "https"; // always set the destination url to use https scOptions.setDestUrl(protocol + uri); // if the port is set, that means the proxy server is used. Then use the http protocol. if (karate.properties['proxy.port']){ protocol = "http"; } var baseUrl = protocol + uri; return { baseUrl : baseUrl } } ``` ================================================ FILE: swagger-coverage-karate/build.gradle.kts ================================================ plugins { java `java-library` } description = "Swagger Coverage Karate" repositories { mavenCentral() } dependencies { api(project(":swagger-coverage-commons")) api(project(":swagger-coverage-commandline")) implementation("io.swagger:swagger-models") implementation("io.swagger.core.v3:swagger-models") implementation("com.intuit.karate:karate-core") //needed for karate runner implementation("com.linecorp.armeria:armeria:1.14.1") implementation("io.netty:netty-all:4.1.74.Final") implementation("org.thymeleaf:thymeleaf:3.0.15.RELEASE") implementation("io.github.classgraph:classgraph:4.8.108") implementation("org.antlr:antlr4-runtime:4.9.3") implementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("junit:junit") testImplementation("org.hamcrest:hamcrest") testImplementation("com.github.tomakehurst:wiremock") } tasks { test { workingDir(buildDir) } } ================================================ FILE: swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/Request.java ================================================ package com.github.viclovsky.swagger.coverage.karate; import java.util.List; import java.util.Map; public class Request { private String baseUrl; private String path; private Map> requestParams; private Map> headerParams; private Map>> requestParts; private Map pathParams; private Boolean hasBody; private String method; private int statusCode; private Map> responseHeaders; // #region Getter/Setter public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String uri) { this.baseUrl = uri; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public Map> getRequestParams() { return requestParams; } public void setRequestParams(Map> requestParams) { this.requestParams = requestParams; } public Map>> getRequestParts() { return requestParts; } public void setRequestParts(Map>> requestParts) { this.requestParts = requestParts; } public Map getPathParams() { return pathParams; } public void setPathParams(Map pathParams) { this.pathParams = pathParams; } public Map> getHeaderParams() { return headerParams; } public void setHeaderParams(Map> headerParams) { this.headerParams = headerParams; } public Map> getResponseHeaders() { return responseHeaders; } public void setResponseHeaders(Map> responseHeaders) { this.responseHeaders = responseHeaders; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public Boolean hasBody() { return hasBody; } public void setHasBody(Boolean hasBody) { this.hasBody = hasBody; } // #endregion } ================================================ FILE: swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/RequestWriter.java ================================================ package com.github.viclovsky.swagger.coverage.karate; import static io.swagger.models.Scheme.forValue; import java.io.File; import java.net.URI; import java.util.List; import java.util.Map; import com.github.viclovsky.swagger.coverage.CoverageOutputWriter; import com.github.viclovsky.swagger.coverage.FileSystemOutputWriter; import com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants; import io.swagger.models.*; import io.swagger.models.parameters.*; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; public class RequestWriter { private CoverageOutputWriter writer; public RequestWriter(String workingDir){ File dir = new File(workingDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); writer = new FileSystemOutputWriter(dir.toPath()); } public void write(Request request, Boolean oas3){ if (oas3){ writeOAS3(request); } else{ writeSwagger(request); } } public void writeSwagger(Request request) { Map> headerParams = request.getHeaderParams(); Map> requestParams = request.getRequestParams(); Map>> requestParts = request.getRequestParts(); Map> responseHeaders = request.getResponseHeaders(); Map pathParams = request.getPathParams(); Operation operation = new Operation(); headerParams.forEach((n, v) -> operation.addParameter(new HeaderParameter().name(n))); if (pathParams != null){ pathParams.forEach((n, v) -> operation.addParameter(new PathParameter().name(n))); } if (request.hasBody()) { operation.addConsumes(getContentType(headerParams)); operation.addParameter(new BodyParameter().name("body")); requestParams.forEach((n, v) -> operation.addParameter(new FormParameter().name(n))); if (requestParts != null) { requestParts.forEach((n, v) -> operation.addParameter(new FormParameter().name(n))); } } else { requestParams.forEach((n, v) -> operation.addParameter(new QueryParameter().name(n))); } if (responseHeaders.containsKey("content-type")) { operation.addProduces(getContentType(responseHeaders)); } operation.addResponse(Integer.toString(request.getStatusCode()), new Response()); URI uri = URI.create(request.getBaseUrl()); String path = "/" + trimAfterChar(request.getPath(), "?"); Swagger swagger = new Swagger().scheme(forValue(uri.getScheme())).host(uri.getHost()).path(path, new Path().set(request.getMethod().toLowerCase(), operation)); writer.write(swagger); } public void writeOAS3(Request request){ Map> headerParams = request.getHeaderParams(); Map> requestParams = request.getRequestParams(); Map>> requestParts = request.getRequestParts(); Map> responseHeaders = request.getResponseHeaders(); Map pathParams = request.getPathParams(); io.swagger.v3.oas.models.Operation operation = new io.swagger.v3.oas.models.Operation(); headerParams.forEach((n, v) -> operation .addParametersItem(new io.swagger.v3.oas.models.parameters.HeaderParameter().name(n))); if (pathParams != null){ pathParams.forEach((n, v) -> operation .addParametersItem(new io.swagger.v3.oas.models.parameters.PathParameter().name(n))); } if (request.hasBody()) { MediaType mediaType = new MediaType(); Schema schema = new Schema<>(); requestParams.forEach((n, v) -> schema.addProperties(n, new Schema<>())); if (requestParts != null) { requestParts.forEach((n, v) -> schema.addProperties(n, new Schema<>())); } mediaType.setSchema(schema); operation.requestBody( new RequestBody().content(new Content().addMediaType(getContentType(headerParams), mediaType))); } else { requestParams.forEach((n, v) -> operation .addParametersItem(new io.swagger.v3.oas.models.parameters.QueryParameter().name(n))); } operation.responses(new ApiResponses().addApiResponse(Integer.toString(request.getStatusCode()), new ApiResponse() .content(new Content().addMediaType(getContentType(responseHeaders), new MediaType())))); URI uri = URI.create(request.getBaseUrl()); String path = "/" + trimAfterChar(request.getPath(), "?"); PathItem pathItem = new PathItem(); pathItem.operation(PathItem.HttpMethod.valueOf(request.getMethod().toUpperCase()), operation); OpenAPI openAPI = new OpenAPI().addServersItem(new Server().url(uri.getHost())) .path(path, pathItem); writer.write(openAPI); } private String getContentType(Map> headerList) { if (!headerList.containsKey("content-type")) { return ""; } String contentType = headerList.get("content-type").get(0); return trimAfterChar(contentType, ";"); } private String trimAfterChar(String s, String character) { return s.substring(0, s.contains(character) ? s.lastIndexOf(character) : s.length()); } } ================================================ FILE: swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageOptions.java ================================================ package com.github.viclovsky.swagger.coverage.karate; public class SwaggerCoverageOptions { private static String destUrl; private static String pathPattern = ""; private static boolean ignoreCall = false; public static void setDestinationURL(String url){ destUrl = url; } public static String getDestinationURL(){ return destUrl; } public static void reset(){ pathPattern = ""; ignoreCall = false; } public static void setPathPattern(String pattern){ pathPattern = pattern; } public static String getPathPattern(){ return pathPattern; } public static void setIgnoreCall(boolean ignore){ ignoreCall = ignore; } public static boolean getIgnoreCall(){ return ignoreCall; } } ================================================ FILE: swagger-coverage-karate/src/main/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageRunner.java ================================================ package com.github.viclovsky.swagger.coverage.karate; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; import com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants; import com.github.viclovsky.swagger.coverage.core.generator.Generator; import com.intuit.karate.FileUtils; import com.intuit.karate.Logger; import com.intuit.karate.Results; import com.intuit.karate.Runner; import com.intuit.karate.core.MockServer; import com.intuit.karate.job.JobConfig; public class SwaggerCoverageRunner extends Runner { private static final Logger logger = new Logger(); private static MockServer mockServer; private static int startProxy(Map args){ mockServer = MockServer .feature("classpath:httpProxy.feature") .args(args) .http(0).build(); return mockServer.getPort(); } private static void stopProxy(){ if (mockServer != null){ mockServer.stop(); } } public static class SwaggerCoverageBuilder extends Builder { final String SPECIFICATION_NAME = "swagger-specification"; final String CONFIG_NAME = "swagger-coverage-config.json"; boolean oas3; URI specificationPath; String configPath; String inputPath; String coverageDir; boolean backupCoverageOutput; private void prepareTests(){ if(backupCoverageOutput){ backupCoverageOutput(); } File outputDir = new File(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); if (outputDir.exists()) FileUtils.deleteDirectory(outputDir); Map args = new HashMap(); args.put("oas3", oas3); args.put("workingDir", coverageDir); int proxyPort = startProxy(args); systemProperty("proxy.port", proxyPort + ""); logger.info("Started proxy at port: {}", proxyPort); } private void backupCoverageOutput(){ File file = new File(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); if (file.exists()) { File newDir = new File(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY + "_" + System.currentTimeMillis()); if (file.renameTo(newDir)){ logger.info("backed up existing swagger-coverage-output dir to: {}", newDir); } else { logger.warn("failed to backup existing swagger-coverage-output dir: {}", file); } } } private void generateReport() { if (coverageDir == null) coverageDir = ""; Path inputPath = Paths.get(coverageDir, SwaggerCoverageConstants.OUTPUT_DIRECTORY); if (!inputPath.toFile().exists()){ try { Files.createDirectory(inputPath); } catch (IOException e) { e.printStackTrace(); logger.warn("Failed to create empty directory for coverage input: {}. Report could not be generated.", inputPath); return; } } Generator generator = new Generator() .setInputPath(inputPath); if (specificationPath != null) { generator.setSpecPath(specificationPath); } else{ File specFile = Optional.of(Paths.get(coverageDir, SPECIFICATION_NAME + ".json").toFile()) .filter((file) -> file.exists()) .orElseGet(()-> Paths.get(coverageDir, SPECIFICATION_NAME + ".yaml").toFile()); if (!specFile.exists()){ throw new NoSuchElementException(); } generator.setSpecPath(specFile.toURI()); } if (configPath != null){ generator.setConfigurationPath(Paths.get(configPath)); } else{ File configFile = Paths.get(coverageDir, CONFIG_NAME).toFile(); if (configFile.exists()){ generator.setConfigurationPath(configFile.toPath()); } } generator.run(); } public SwaggerCoverageBuilder swagger(){ oas3 = false; return this; } public SwaggerCoverageBuilder oas3(){ oas3 = true; return this; } public SwaggerCoverageBuilder swaggerSpec(URI path){ specificationPath = path; return this; } public SwaggerCoverageBuilder swaggerCoverageConfig(String configPath){ this.configPath = configPath; return this; } public SwaggerCoverageBuilder coverageDir(String coverageDir){ this.coverageDir = coverageDir; return this; } public SwaggerCoverageBuilder backupCoverageOutput(boolean value){ this.backupCoverageOutput = value; return this; } @Override public Results parallel(int threadCount){ prepareTests(); Results results = super.parallel(threadCount); generateReport(); stopProxy(); return results; } @Override public Results jobManager(JobConfig value){ prepareTests(); Results results = super.jobManager(value); generateReport(); stopProxy(); return results; } } public static SwaggerCoverageBuilder path(String... paths) { SwaggerCoverageBuilder builder = new SwaggerCoverageBuilder(); return builder.path(paths); } public static SwaggerCoverageBuilder path(List paths) { SwaggerCoverageBuilder builder = new SwaggerCoverageBuilder(); return builder.path(paths); } public static SwaggerCoverageBuilder builder() { return new SwaggerCoverageBuilder(); } } ================================================ FILE: swagger-coverage-karate/src/main/resources/httpProxy.feature ================================================ @ignore Feature: Http Proxy for Swagger-Coverage Background: * print 'using oas3:', oas3 * print 'working Dir:', workingDir * def initWriter = """ function(dir){ var RequestWriter = Java.type('com.github.viclovsky.swagger.coverage.karate.RequestWriter'); return new RequestWriter(dir); } """ * def writer = callonce initWriter workingDir * def scOptions = Java.type("com.github.viclovsky.swagger.coverage.karate.SwaggerCoverageOptions"); Scenario: scOptions.getIgnoreCall() * print "Call ignored for Swagger Coverage Report." * karate.proceed(scOptions.getDestinationURL()) * scOptions.reset() Scenario: * karate.proceed(scOptions.getDestinationURL()) * def pathParams = pathMatches(scOptions.getPathPattern()) ? pathParams : null * def multipart = karate.get('requestParts', null) * if (multipart != null) karate.remove("multipart", ".[*].value") * def reqJson = """ { baseUrl: '#(requestUrlBase)', path: '#(requestUri)', requestParams: '#(requestParams)', headerParams: '#(requestHeaders)', responseHeaders: '#(responseHeaders)', requestParts: '#(multipart)', pathParams: '#(pathParams)', method: '#(requestMethod)', statusCode: '#(responseStatus)', hasBody: '#(request != null)' } """ * def coverageRequest = karate.toBean(reqJson, 'com.github.viclovsky.swagger.coverage.karate.Request') * eval writer.write(coverageRequest, oas3) * scOptions.reset() ================================================ FILE: swagger-coverage-karate/src/main/resources/karate-base.js ================================================ function fn(){ var proxyPort = karate.properties['proxy.port']; if(proxyPort){ karate.configure('proxy', 'http://127.0.0.1:'+ proxyPort); } var setDestUrl = function(arg) { var SCO = Java.type("com.github.viclovsky.swagger.coverage.karate.SwaggerCoverageOptions"); SCO.setDestinationURL(arg); } var setPathPattern = function(arg) { var SCO = Java.type("com.github.viclovsky.swagger.coverage.karate.SwaggerCoverageOptions"); SCO.setPathPattern(arg); } var ignoreNextCall = function() { var SCO = Java.type("com.github.viclovsky.swagger.coverage.karate.SwaggerCoverageOptions"); SCO.setIgnoreCall(true); } return { scOptions : { setDestUrl : setDestUrl, setPathPattern : setPathPattern, ignoreNextCall : ignoreNextCall } } } ================================================ FILE: swagger-coverage-karate/src/test/java/com/github/viclovsky/swagger/coverage/karate/RequestWriterTest.java ================================================ package com.github.viclovsky.swagger.coverage.karate; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.OUTPUT_DIRECTORY; import static java.util.Optional.ofNullable; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.iterableWithSize; import static org.hamcrest.Matchers.endsWith; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import com.intuit.karate.JsonUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class RequestWriterTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); private Request request = new Request(); @Before public void setUp() { try { File file = ofNullable(getClass().getClassLoader().getResource("request.json")) .map(resource -> new File(resource.getFile())) .orElseThrow(() -> new IllegalArgumentException("Unable to read file: request.json")); String requestJson = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); request = JsonUtils.fromJson(requestJson, Request.class); } catch (Exception e) { throw new RuntimeException("Can't load request json from resources folder!"); } } @Test public void shouldWriteSwaggerJson() throws IOException { Path workingDir = folder.newFolder().toPath(); RequestWriter writer = new RequestWriter(workingDir.toString()); writer.write(request, false); assertThat(getPaths(workingDir.resolve(OUTPUT_DIRECTORY)), iterableWithSize(1)); assertThat(getPaths(workingDir.resolve(OUTPUT_DIRECTORY)).get(0).toString(), endsWith(".json")); } @Test public void shouldWriteOas3Yaml() throws IOException { Path workingDir = folder.newFolder().toPath(); RequestWriter writer = new RequestWriter(workingDir.toString()); writer.write(request, true); assertThat(getPaths(workingDir.resolve(OUTPUT_DIRECTORY)), iterableWithSize(1)); assertThat(getPaths(workingDir.resolve(OUTPUT_DIRECTORY)).get(0).toString(), endsWith(".yaml")); } private List getPaths(Path path) { try (Stream paths = Files.walk(path)) { return paths.filter(Files::isRegularFile).collect(Collectors.toList()); } catch (IOException e) { throw new RuntimeException("can't walk files in swagger output directory"); } } } ================================================ FILE: swagger-coverage-karate/src/test/java/com/github/viclovsky/swagger/coverage/karate/SwaggerCoverageRunnerTest.java ================================================ package com.github.viclovsky.swagger.coverage.karate; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants; import com.intuit.karate.FileUtils; import com.intuit.karate.Results; import org.apache.http.HttpStatus; import org.hamcrest.io.FileMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.COVERAGE_HTML_REPORT_NAME; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.hamcrest.Matchers.iterableWithSize; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; public class SwaggerCoverageRunnerTest { private static final String BODY_STRING = "{ name: MockPet }"; @Rule public WireMockRule mock = new WireMockRule(options().dynamicPort().withRootDirectory(getDirFromResources("/wiremock").toString()), false); @Rule public TemporaryFolder folder = new TemporaryFolder(); @Before public void setUp(){ configureFor(mock.port()); stubFor(get(urlMatching("/pet/.*")) .willReturn(aResponse().withStatus(HttpStatus.SC_OK) .withBody(BODY_STRING))); deleteOutputDirs(getDirFromResources("/api-test-coverage-v2")); deleteOutputDirs(getDirFromResources("/api-test-coverage-v3")); } @Test public void shouldGetSpecFromUrlV2() throws IOException{ File tempCoverageDir = folder.newFolder(); Results results = SwaggerCoverageRunner.path("classpath:petv2.feature") .backupReportDir(false) .coverageDir(tempCoverageDir.toString()) .swaggerSpec(URI.create(mock.url("/swagger.json"))) .swagger() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void shouldGetSpecFromUrlV3() throws IOException{ File tempCoverageDir = folder.newFolder(); Results results = SwaggerCoverageRunner.path("classpath:petv3.feature") .backupReportDir(false) .coverageDir(tempCoverageDir.toString()) .swaggerSpec(URI.create(mock.url("/openapi.yaml"))) .oas3() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void shouldGetFilesFromSpecifiedFilesV2(){ Path coverageDir = getDirFromResources("/api-test-coverage-v2"); Results results = SwaggerCoverageRunner.path("classpath:petv2.feature") .backupReportDir(false) .coverageDir(coverageDir.toString()) .swaggerSpec(coverageDir.resolve("swagger-specification.json").toUri()) .swaggerCoverageConfig(coverageDir.resolve("swagger-coverage-config.json").toString()) .swagger() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void shouldGetFilesFromSpecifiedFilesV3(){ Path coverageDir = getDirFromResources("/api-test-coverage-v3"); Results results = SwaggerCoverageRunner.path("classpath:petv3.feature") .backupReportDir(false) .coverageDir(coverageDir.toString()) .swaggerSpec(coverageDir.resolve("swagger-specification.yaml").toUri()) .swaggerCoverageConfig(coverageDir.resolve("swagger-coverage-config.json").toString()) .oas3() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void shouldGetFilesFromSpecifiedDirV2(){ Path coverageDir = getDirFromResources("/api-test-coverage-v2"); Results results = SwaggerCoverageRunner.path("classpath:petv2.feature") .backupReportDir(false) .coverageDir(coverageDir.toString()) .swagger() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void shouldGetFilesFromSpecifiedDirV3(){ Path coverageDir = getDirFromResources("/api-test-coverage-v3"); Results results = SwaggerCoverageRunner.path("classpath:petv3.feature") .backupReportDir(false) .coverageDir(coverageDir.toString()) .oas3() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); } @Test public void shouldBackupCoverageOutput() { Path coverageDir = getDirFromResources("/api-test-coverage-v3"); Results results = SwaggerCoverageRunner.path("classpath:petv3.feature") .backupReportDir(false) .coverageDir(coverageDir.toString()) .oas3() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); results = SwaggerCoverageRunner.path("classpath:petv3.feature") .backupReportDir(false) .backupCoverageOutput(true) .coverageDir(coverageDir.toString()) .oas3() .systemProperty("baseUrl", mock.baseUrl()) .parallel(1); assertEquals(results.getErrorMessages(), 0, results.getFailCount()); assertThat(Paths.get(COVERAGE_HTML_REPORT_NAME).toFile(), FileMatchers.anExistingFile()); assertThat(Arrays.asList(coverageDir.toFile().list((dir, name) -> name.contains(SwaggerCoverageConstants.OUTPUT_DIRECTORY))), iterableWithSize(2)); } private Path getDirFromResources(String name) { try { return Paths.get(this.getClass().getResource(name).toURI()); } catch (URISyntaxException e) { return null; } } private void deleteOutputDirs(Path coverageDir){ String[] dirs = coverageDir.toFile().list((dir, name) -> name.contains(SwaggerCoverageConstants.OUTPUT_DIRECTORY)); for (String dir : dirs){ FileUtils.deleteDirectory(coverageDir.resolve(dir).toFile()); } } } ================================================ FILE: swagger-coverage-karate/src/test/resources/api-test-coverage-v2/swagger-coverage-config.json ================================================ { "rules" : { }, "writers" : { "html": { "locale": "en", "filename":"swagger-coverage-report.html", "numberFormat": "0.##" } } } ================================================ FILE: swagger-coverage-karate/src/test/resources/api-test-coverage-v2/swagger-specification.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders" }, { "name": "user", "description": "Operations about user", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } } ], "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": true, "type": "array", "items": { "type": "string", "enum": [ "available", "pending", "sold" ], "default": "available" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": true, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-karate/src/test/resources/api-test-coverage-v3/swagger-coverage-config.json ================================================ { "rules" : { }, "writers" : { "html": { "locale": "en", "filename":"swagger-coverage-report.html", "numberFormat": "0.##" } } } ================================================ FILE: swagger-coverage-karate/src/test/resources/api-test-coverage-v3/swagger-specification.yaml ================================================ openapi: 3.0.2 info: title: Swagger Petstore - OpenAPI 3.0 description: "This is a sample Pet Store Server based on the OpenAPI 3.0 specification.\ \ You can find out more about\nSwagger at [http://swagger.io](http://swagger.io).\ \ In the third iteration of the pet store, we've switched to the design first\ \ approach!\nYou can now help us improve the API whether it's by making changes\ \ to the definition itself or to the code.\nThat way, with time, we can improve\ \ the API in general, and expose some of the new features in OAS3.\n\nSome useful\ \ links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n\ - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)" termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html version: 1.0.7 externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: /api/v3 tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: http://swagger.io - name: store description: Operations about user - name: user description: Access to Petstore orders externalDocs: description: Find out more about our store url: http://swagger.io paths: /pet: put: tags: - pet summary: Update an existing pet description: Update an existing pet by Id operationId: updatePet requestBody: description: Update an existent pet in the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Pet' required: true responses: "200": description: Successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets post: tags: - pet summary: Add a new pet to the store description: Add a new pet to the store operationId: addPet requestBody: description: Create a new pet in the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Pet' required: true responses: "200": description: Successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: false explode: true schema: type: string default: available enum: - available - pending - sold responses: "200": description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: "Multiple tags can be provided with comma separated strings. Use\ \ tag1, tag2, tag3 for testing." operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: false explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets /pet/{petId}: get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] - petstore_auth: - write:pets - read:pets post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 - name: name in: query description: Name of pet that needs to be updated schema: type: string - name: status in: query description: Status of pet that needs to be updated schema: type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header description: "" required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid pet value security: - petstore_auth: - write:pets - read:pets /pet/{petId}/uploadImage: post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 - name: additionalMetadata in: query description: Additional Metadata required: false schema: type: string requestBody: content: application/octet-stream: schema: type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' security: - petstore_auth: - write:pets - read:pets /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] /store/order: post: tags: - store summary: Place an order for a pet description: Place a new order in the store operationId: placeOrder requestBody: content: application/json: schema: $ref: '#/components/schemas/Order' application/xml: schema: $ref: '#/components/schemas/Order' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Order' responses: "200": description: successful operation content: application/json: schema: $ref: '#/components/schemas/Order' "405": description: Invalid input /store/order/{orderId}: get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of order that needs to be fetched required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Order not found /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: description: Created user object content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/User' responses: default: description: successful operation content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: Creates list of users with given input array operationId: createUsersWithListInput requestBody: content: application/json: schema: type: array items: $ref: '#/components/schemas/User' responses: "200": description: Successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' default: description: successful operation /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: false schema: type: string - name: password in: query description: The password for login in clear text required: false schema: type: string responses: "200": description: successful operation headers: X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time content: application/xml: schema: type: string application/json: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser parameters: [] responses: default: description: successful operation /user/{username}: get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: 'The name that needs to be fetched. Use user1 for testing. ' required: true schema: type: string responses: "200": description: successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Update user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be deleted required: true schema: type: string requestBody: description: Update an existent user in the store content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/User' responses: default: description: successful operation delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found components: schemas: Order: type: object properties: id: type: integer format: int64 example: 10 petId: type: integer format: int64 example: 198772 quantity: type: integer format: int32 example: 7 shipDate: type: string format: date-time status: type: string description: Order Status example: approved enum: - placed - approved - delivered complete: type: boolean xml: name: order Customer: type: object properties: id: type: integer format: int64 example: 100000 username: type: string example: fehguy address: type: array xml: name: addresses wrapped: true items: $ref: '#/components/schemas/Address' xml: name: customer Address: type: object properties: street: type: string example: 437 Lytton city: type: string example: Palo Alto state: type: string example: CA zip: type: string example: "94301" xml: name: address Category: type: object properties: id: type: integer format: int64 example: 1 name: type: string example: Dogs xml: name: category User: type: object properties: id: type: integer format: int64 example: 10 username: type: string example: theUser firstName: type: string example: John lastName: type: string example: James email: type: string example: john@email.com password: type: string example: "12345" phone: type: string example: "12345" userStatus: type: integer description: User Status format: int32 example: 1 xml: name: user Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: tag Pet: required: - name - photoUrls type: object properties: id: type: integer format: int64 example: 10 name: type: string example: doggie category: $ref: '#/components/schemas/Category' photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: '#/components/schemas/Tag' status: type: string description: pet status in the store enum: - available - pending - sold xml: name: pet ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string xml: name: '##default' requestBodies: Pet: description: Pet object that needs to be added to the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' UserArray: description: List of user object content: application/json: schema: type: array items: $ref: '#/components/schemas/User' securitySchemes: petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore3.swagger.io/oauth/authorize scopes: write:pets: modify pets in your account read:pets: read your pets api_key: type: apiKey name: api_key in: header ================================================ FILE: swagger-coverage-karate/src/test/resources/petv2.feature ================================================ Feature: Petstore v2 Background: * def baseUrl = karate.properties["baseUrl"] * url baseUrl * eval scOptions.setDestUrl(baseUrl) Scenario: Basic Test * eval scOptions.setPathPattern("/pet/{id}") Given path "pet", 1 When method GET Then status 200 ================================================ FILE: swagger-coverage-karate/src/test/resources/petv3.feature ================================================ Feature: Petstore v3 Background: * def baseUrl = karate.properties["baseUrl"] * url baseUrl * eval scOptions.setDestUrl(baseUrl) Scenario: Basic Test * eval scOptions.setPathPattern("/pet/{petId}") Given path "pet", 2 When method GET Then status 200 ================================================ FILE: swagger-coverage-karate/src/test/resources/request.json ================================================ { "baseUrl": "petstore.swagger.io/v2", "path": "pet", "requestParams": { "param1": [ "value1" ], "param2": [ "23" ] }, "headerParams": { "host": [ "service-reports.iot.qbeyond.tech" ], "accept-encoding": [ "gzip,deflate" ] }, "responseHeaders": { "content-type": [ "application/json" ], "content-length": [ "28" ] }, "requestParts": { "file": [ { "charset": "UTF-8", "filename": "testImage", "transferEncoding": "binary", "name": "file", "contentType": "image/jpeg" } ] }, "method": "POST", "statusCode": 200, "hasBody": false } ================================================ FILE: swagger-coverage-karate/src/test/resources/wiremock/__files/openapi.yaml ================================================ openapi: 3.0.2 info: title: Swagger Petstore - OpenAPI 3.0 description: "This is a sample Pet Store Server based on the OpenAPI 3.0 specification.\ \ You can find out more about\nSwagger at [http://swagger.io](http://swagger.io).\ \ In the third iteration of the pet store, we've switched to the design first\ \ approach!\nYou can now help us improve the API whether it's by making changes\ \ to the definition itself or to the code.\nThat way, with time, we can improve\ \ the API in general, and expose some of the new features in OAS3.\n\nSome useful\ \ links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n\ - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)" termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io license: name: Apache 2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html version: 1.0.7 externalDocs: description: Find out more about Swagger url: http://swagger.io servers: - url: /api/v3 tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more url: http://swagger.io - name: store description: Operations about user - name: user description: Access to Petstore orders externalDocs: description: Find out more about our store url: http://swagger.io paths: /pet: put: tags: - pet summary: Update an existing pet description: Update an existing pet by Id operationId: updatePet requestBody: description: Update an existent pet in the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Pet' required: true responses: "200": description: Successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' "400": description: Invalid ID supplied "404": description: Pet not found "405": description: Validation exception security: - petstore_auth: - write:pets - read:pets post: tags: - pet summary: Add a new pet to the store description: Add a new pet to the store operationId: addPet requestBody: description: Create a new pet in the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Pet' required: true responses: "200": description: Successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: false explode: true schema: type: string default: available enum: - available - pending - sold responses: "200": description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' "400": description: Invalid status value security: - petstore_auth: - write:pets - read:pets /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: "Multiple tags can be provided with comma separated strings. Use\ \ tag1, tag2, tag3 for testing." operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: false explode: true schema: type: array items: type: string responses: "200": description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' "400": description: Invalid tag value security: - petstore_auth: - write:pets - read:pets /pet/{petId}: get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' "400": description: Invalid ID supplied "404": description: Pet not found security: - api_key: [] - petstore_auth: - write:pets - read:pets post: tags: - pet summary: Updates a pet in the store with form data description: "" operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 - name: name in: query description: Name of pet that needs to be updated schema: type: string - name: status in: query description: Status of pet that needs to be updated schema: type: string responses: "405": description: Invalid input security: - petstore_auth: - write:pets - read:pets delete: tags: - pet summary: Deletes a pet description: "" operationId: deletePet parameters: - name: api_key in: header description: "" required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: "400": description: Invalid pet value security: - petstore_auth: - write:pets - read:pets /pet/{petId}/uploadImage: post: tags: - pet summary: uploads an image description: "" operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 - name: additionalMetadata in: query description: Additional Metadata required: false schema: type: string requestBody: content: application/octet-stream: schema: type: string format: binary responses: "200": description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' security: - petstore_auth: - write:pets - read:pets /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: "200": description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] /store/order: post: tags: - store summary: Place an order for a pet description: Place a new order in the store operationId: placeOrder requestBody: content: application/json: schema: $ref: '#/components/schemas/Order' application/xml: schema: $ref: '#/components/schemas/Order' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Order' responses: "200": description: successful operation content: application/json: schema: $ref: '#/components/schemas/Order' "405": description: Invalid input /store/order/{orderId}: get: tags: - store summary: Find purchase order by ID description: For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of order that needs to be fetched required: true schema: type: integer format: int64 responses: "200": description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' "400": description: Invalid ID supplied "404": description: Order not found delete: tags: - store summary: Delete purchase order by ID description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: integer format: int64 responses: "400": description: Invalid ID supplied "404": description: Order not found /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser requestBody: description: Created user object content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/User' responses: default: description: successful operation content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: Creates list of users with given input array operationId: createUsersWithListInput requestBody: content: application/json: schema: type: array items: $ref: '#/components/schemas/User' responses: "200": description: Successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' default: description: successful operation /user/login: get: tags: - user summary: Logs user into the system description: "" operationId: loginUser parameters: - name: username in: query description: The user name for login required: false schema: type: string - name: password in: query description: The password for login in clear text required: false schema: type: string responses: "200": description: successful operation headers: X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time content: application/xml: schema: type: string application/json: schema: type: string "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: "" operationId: logoutUser parameters: [] responses: default: description: successful operation /user/{username}: get: tags: - user summary: Get user by user name description: "" operationId: getUserByName parameters: - name: username in: path description: 'The name that needs to be fetched. Use user1 for testing. ' required: true schema: type: string responses: "200": description: successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' "400": description: Invalid username supplied "404": description: User not found put: tags: - user summary: Update user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be deleted required: true schema: type: string requestBody: description: Update an existent user in the store content: application/json: schema: $ref: '#/components/schemas/User' application/xml: schema: $ref: '#/components/schemas/User' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/User' responses: default: description: successful operation delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: "400": description: Invalid username supplied "404": description: User not found components: schemas: Order: type: object properties: id: type: integer format: int64 example: 10 petId: type: integer format: int64 example: 198772 quantity: type: integer format: int32 example: 7 shipDate: type: string format: date-time status: type: string description: Order Status example: approved enum: - placed - approved - delivered complete: type: boolean xml: name: order Customer: type: object properties: id: type: integer format: int64 example: 100000 username: type: string example: fehguy address: type: array xml: name: addresses wrapped: true items: $ref: '#/components/schemas/Address' xml: name: customer Address: type: object properties: street: type: string example: 437 Lytton city: type: string example: Palo Alto state: type: string example: CA zip: type: string example: "94301" xml: name: address Category: type: object properties: id: type: integer format: int64 example: 1 name: type: string example: Dogs xml: name: category User: type: object properties: id: type: integer format: int64 example: 10 username: type: string example: theUser firstName: type: string example: John lastName: type: string example: James email: type: string example: john@email.com password: type: string example: "12345" phone: type: string example: "12345" userStatus: type: integer description: User Status format: int32 example: 1 xml: name: user Tag: type: object properties: id: type: integer format: int64 name: type: string xml: name: tag Pet: required: - name - photoUrls type: object properties: id: type: integer format: int64 example: 10 name: type: string example: doggie category: $ref: '#/components/schemas/Category' photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl tags: type: array xml: wrapped: true items: $ref: '#/components/schemas/Tag' status: type: string description: pet status in the store enum: - available - pending - sold xml: name: pet ApiResponse: type: object properties: code: type: integer format: int32 type: type: string message: type: string xml: name: '##default' requestBodies: Pet: description: Pet object that needs to be added to the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' UserArray: description: List of user object content: application/json: schema: type: array items: $ref: '#/components/schemas/User' securitySchemes: petstore_auth: type: oauth2 flows: implicit: authorizationUrl: https://petstore3.swagger.io/oauth/authorize scopes: write:pets: modify pets in your account read:pets: read your pets api_key: type: apiKey name: api_key in: header ================================================ FILE: swagger-coverage-karate/src/test/resources/wiremock/__files/swagger.json ================================================ { "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", "version": "1.0.3", "title": "Swagger Petstore", "termsOfService": "http://swagger.io/terms/", "contact": { "email": "apiteam@swagger.io" }, "license": { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } }, "host": "petstore.swagger.io", "basePath": "/v2", "tags": [ { "name": "pet", "description": "Everything about your Pets", "externalDocs": { "description": "Find out more", "url": "http://swagger.io" } }, { "name": "store", "description": "Access to Petstore orders" }, { "name": "user", "description": "Operations about user", "externalDocs": { "description": "Find out more about our store", "url": "http://swagger.io" } } ], "schemes": [ "https", "http" ], "paths": { "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "type": "integer", "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Pet" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "api_key": [] } ] }, "post": { "tags": [ "pet" ], "summary": "Updates a pet in the store with form data", "description": "", "operationId": "updatePetWithForm", "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet that needs to be updated", "required": true, "type": "integer", "format": "int64" }, { "name": "name", "in": "formData", "description": "Updated name of the pet", "required": false, "type": "string" }, { "name": "status", "in": "formData", "description": "Updated status of the pet", "required": false, "type": "string" } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "delete": { "tags": [ "pet" ], "summary": "Deletes a pet", "description": "", "operationId": "deletePet", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "api_key", "in": "header", "required": false, "type": "string" }, { "name": "petId", "in": "path", "description": "Pet id to delete", "required": true, "type": "integer", "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/{petId}/uploadImage": { "post": { "tags": [ "pet" ], "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to update", "required": true, "type": "integer", "format": "int64" }, { "name": "additionalMetadata", "in": "formData", "description": "Additional data to pass to server", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": false, "type": "file" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/ApiResponse" } } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet": { "post": { "tags": [ "pet" ], "summary": "Add a new pet to the store", "description": "", "operationId": "addPet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "405": { "description": "Invalid input" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] }, "put": { "tags": [ "pet" ], "summary": "Update an existing pet", "description": "", "operationId": "updatePet", "consumes": [ "application/json", "application/xml" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Pet object that needs to be added to the store", "required": true, "schema": { "$ref": "#/definitions/Pet" } } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Pet not found" }, "405": { "description": "Validation exception" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByStatus": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "status", "in": "query", "description": "Status values that need to be considered for filter", "required": true, "type": "array", "items": { "type": "string", "enum": [ "available", "pending", "sold" ], "default": "available" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid status value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] } }, "/pet/findByTags": { "get": { "tags": [ "pet" ], "summary": "Finds Pets by tags", "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId": "findPetsByTags", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "tags", "in": "query", "description": "Tags to filter by", "required": true, "type": "array", "items": { "type": "string" }, "collectionFormat": "multi" } ], "responses": { "200": { "description": "successful operation", "schema": { "type": "array", "items": { "$ref": "#/definitions/Pet" } } }, "400": { "description": "Invalid tag value" } }, "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ], "deprecated": true } }, "/store/inventory": { "get": { "tags": [ "store" ], "summary": "Returns pet inventories by status", "description": "Returns a map of status codes to quantities", "operationId": "getInventory", "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "description": "successful operation", "schema": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } }, "security": [ { "api_key": [] } ] } }, "/store/order/{orderId}": { "get": { "tags": [ "store" ], "summary": "Find purchase order by ID", "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", "operationId": "getOrderById", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of pet that needs to be fetched", "required": true, "type": "integer", "maximum": 10, "minimum": 1, "format": "int64" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } }, "delete": { "tags": [ "store" ], "summary": "Delete purchase order by ID", "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", "operationId": "deleteOrder", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "orderId", "in": "path", "description": "ID of the order that needs to be deleted", "required": true, "type": "integer", "minimum": 1, "format": "int64" } ], "responses": { "400": { "description": "Invalid ID supplied" }, "404": { "description": "Order not found" } } } }, "/store/order": { "post": { "tags": [ "store" ], "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "order placed for purchasing the pet", "required": true, "schema": { "$ref": "#/definitions/Order" } } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/Order" } }, "400": { "description": "Invalid Order" } } } }, "/user/{username}": { "get": { "tags": [ "user" ], "summary": "Get user by user name", "description": "", "operationId": "getUserByName", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be fetched. Use user1 for testing. ", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "schema": { "$ref": "#/definitions/User" } }, "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } }, "put": { "tags": [ "user" ], "summary": "Updated user", "description": "This can only be done by the logged in user.", "operationId": "updateUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "name that need to be updated", "required": true, "type": "string" }, { "in": "body", "name": "body", "description": "Updated user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "400": { "description": "Invalid user supplied" }, "404": { "description": "User not found" } } }, "delete": { "tags": [ "user" ], "summary": "Delete user", "description": "This can only be done by the logged in user.", "operationId": "deleteUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "path", "description": "The name that needs to be deleted", "required": true, "type": "string" } ], "responses": { "400": { "description": "Invalid username supplied" }, "404": { "description": "User not found" } } } }, "/user/login": { "get": { "tags": [ "user" ], "summary": "Logs user into the system", "description": "", "operationId": "loginUser", "produces": [ "application/json", "application/xml" ], "parameters": [ { "name": "username", "in": "query", "description": "The user name for login", "required": true, "type": "string" }, { "name": "password", "in": "query", "description": "The password for login in clear text", "required": true, "type": "string" } ], "responses": { "200": { "description": "successful operation", "headers": { "X-Expires-After": { "type": "string", "format": "date-time", "description": "date in UTC when token expires" }, "X-Rate-Limit": { "type": "integer", "format": "int32", "description": "calls per hour allowed by the user" } }, "schema": { "type": "string" } }, "400": { "description": "Invalid username/password supplied" } } } }, "/user/logout": { "get": { "tags": [ "user" ], "summary": "Logs out current logged in user session", "description": "", "operationId": "logoutUser", "produces": [ "application/json", "application/xml" ], "parameters": [], "responses": { "default": { "description": "successful operation" } } } }, "/user": { "post": { "tags": [ "user" ], "summary": "Create user", "description": "This can only be done by the logged in user.", "operationId": "createUser", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "Created user object", "required": true, "schema": { "$ref": "#/definitions/User" } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithArray": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithArrayInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } }, "/user/createWithList": { "post": { "tags": [ "user" ], "summary": "Creates list of users with given input array", "description": "", "operationId": "createUsersWithListInput", "consumes": [ "application/json" ], "produces": [ "application/json", "application/xml" ], "parameters": [ { "in": "body", "name": "body", "description": "List of user object", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/User" } } } ], "responses": { "default": { "description": "successful operation" } } } } }, "securityDefinitions": { "api_key": { "type": "apiKey", "name": "api_key", "in": "header" }, "petstore_auth": { "type": "oauth2", "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", "flow": "implicit", "scopes": { "read:pets": "read your pets", "write:pets": "modify pets in your account" } } }, "definitions": { "Category": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Category" } }, "Pet": { "type": "object", "required": [ "name", "photoUrls" ], "properties": { "id": { "type": "integer", "format": "int64" }, "category": { "$ref": "#/definitions/Category" }, "name": { "type": "string", "example": "doggie" }, "photoUrls": { "type": "array", "xml": { "wrapped": true }, "items": { "type": "string", "xml": { "name": "photoUrl" } } }, "tags": { "type": "array", "xml": { "wrapped": true }, "items": { "xml": { "name": "tag" }, "$ref": "#/definitions/Tag" } }, "status": { "type": "string", "description": "pet status in the store", "enum": [ "available", "pending", "sold" ] } }, "xml": { "name": "Pet" } }, "Tag": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" } }, "xml": { "name": "Tag" } }, "ApiResponse": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "type": { "type": "string" }, "message": { "type": "string" } } }, "Order": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "petId": { "type": "integer", "format": "int64" }, "quantity": { "type": "integer", "format": "int32" }, "shipDate": { "type": "string", "format": "date-time" }, "status": { "type": "string", "description": "Order Status", "enum": [ "placed", "approved", "delivered" ] }, "complete": { "type": "boolean" } }, "xml": { "name": "Order" } }, "User": { "type": "object", "properties": { "id": { "type": "integer", "format": "int64" }, "username": { "type": "string" }, "firstName": { "type": "string" }, "lastName": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string" }, "phone": { "type": "string" }, "userStatus": { "type": "integer", "format": "int32", "description": "User Status" } }, "xml": { "name": "User" } } }, "externalDocs": { "description": "Find out more about Swagger", "url": "http://swagger.io" } } ================================================ FILE: swagger-coverage-rest-assured/build.gradle.kts ================================================ plugins { java `java-library` } description = "Swagger Coverage Rest-Assured" repositories { mavenCentral() } dependencies { api(project(":swagger-coverage-commons")) implementation("io.rest-assured:rest-assured") implementation("io.swagger:swagger-models") implementation("io.swagger.core.v3:swagger-models") testImplementation("junit:junit") testImplementation("com.github.tomakehurst:wiremock") testImplementation("org.hamcrest:hamcrest") } tasks { test { //set the workingDir to the build dir so we don't pollute the main project dir //with generated test files workingDir(buildDir) } } ================================================ FILE: swagger-coverage-rest-assured/src/main/java/com/github/viclovsky/swagger/coverage/SwaggerCoverageRestAssured.java ================================================ package com.github.viclovsky.swagger.coverage; import io.restassured.filter.FilterContext; import io.restassured.filter.OrderedFilter; import io.restassured.response.Response; import io.restassured.specification.FilterableRequestSpecification; import io.restassured.specification.FilterableResponseSpecification; import io.swagger.models.Operation; import io.swagger.models.Swagger; import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.FormParameter; import io.swagger.models.parameters.HeaderParameter; import io.swagger.models.parameters.PathParameter; import io.swagger.models.parameters.QueryParameter; import java.net.URI; import java.nio.file.Paths; import java.util.Objects; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.BODY_PARAM_NAME; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.OUTPUT_DIRECTORY; import static io.swagger.models.Scheme.forValue; import static java.lang.String.valueOf; public class SwaggerCoverageRestAssured implements OrderedFilter { private CoverageOutputWriter writer; public SwaggerCoverageRestAssured(CoverageOutputWriter writer) { this.writer = writer; } public SwaggerCoverageRestAssured() { this.writer = new FileSystemOutputWriter(Paths.get(OUTPUT_DIRECTORY)); } @Override public int getOrder() { return Integer.MAX_VALUE; } @Override public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) { Operation operation = new Operation(); requestSpec.getPathParams().forEach((n, v) -> operation.addParameter(new PathParameter().name(n).example(v))); //Ignore ClassCastException for https://github.com/rest-assured/rest-assured/issues/1232 try { requestSpec.getQueryParams().forEach((n, v) -> operation.addParameter(new QueryParameter().name(n).example(v))); } catch (ClassCastException ex) { requestSpec.getQueryParams().keySet().forEach(n -> operation.addParameter(new QueryParameter().name(n))); } try { requestSpec.getFormParams().forEach((n, v) -> operation.addParameter(new FormParameter().name(n).example(v))); } catch (ClassCastException ex) { requestSpec.getFormParams().keySet().forEach((n -> operation.addParameter(new FormParameter().name(n)))); } //end requestSpec.getHeaders().forEach(header -> operation.addParameter(new HeaderParameter().name(header.getName()) .example(header.getValue()))); requestSpec.getMultiPartParams().forEach(multiPartSpecification -> operation.addParameter(new FormParameter() .name(multiPartSpecification.getControlName()))); if (Objects.nonNull(requestSpec.getBody())) { operation.addParameter(new BodyParameter().name(BODY_PARAM_NAME)); } final Response response = ctx.next(requestSpec, responseSpec); operation.addResponse(valueOf(response.statusCode()), new io.swagger.models.Response()); Swagger swagger = new Swagger() .scheme(forValue(URI.create(requestSpec.getURI()).getScheme())) .host(URI.create(requestSpec.getURI()).getHost()) .consumes(requestSpec.getContentType()) .produces(response.getContentType()) .path(requestSpec.getUserDefinedPath(), new io.swagger.models.Path().set(requestSpec.getMethod().toLowerCase(), operation)); writer.write(swagger); return response; } } ================================================ FILE: swagger-coverage-rest-assured/src/main/java/com/github/viclovsky/swagger/coverage/SwaggerCoverageV3RestAssured.java ================================================ package com.github.viclovsky.swagger.coverage; import io.restassured.filter.FilterContext; import io.restassured.filter.OrderedFilter; import io.restassured.response.Response; import io.restassured.specification.FilterableRequestSpecification; import io.restassured.specification.FilterableResponseSpecification; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.PathParameter; import io.swagger.v3.oas.models.parameters.QueryParameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.servers.Server; import java.net.URI; import java.nio.file.Paths; import java.util.Objects; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.OUTPUT_DIRECTORY; import static java.lang.String.valueOf; public class SwaggerCoverageV3RestAssured implements OrderedFilter { private CoverageOutputWriter writer; public SwaggerCoverageV3RestAssured(CoverageOutputWriter writer) { this.writer = writer; } public SwaggerCoverageV3RestAssured() { this.writer = new FileSystemOutputWriter(Paths.get(OUTPUT_DIRECTORY)); } @Override public int getOrder() { return Integer.MAX_VALUE; } @Override public Response filter(FilterableRequestSpecification requestSpec, FilterableResponseSpecification responseSpec, FilterContext ctx) { Operation operation = new Operation(); requestSpec.getPathParams().forEach((n, v) -> operation.addParametersItem(new PathParameter().name(n).example(v))); //Ignore ClassCastException for https://github.com/rest-assured/rest-assured/issues/1232 try { requestSpec.getQueryParams().forEach((n, v) -> operation.addParametersItem(new QueryParameter().name(n).example(v))); } catch (ClassCastException ex) { requestSpec.getQueryParams().keySet().forEach(n -> operation.addParametersItem(new QueryParameter().name(n))); } requestSpec.getHeaders().forEach(header -> operation.addParametersItem(new HeaderParameter().name(header.getName()) .example(header.getValue()))); final Response response = ctx.next(requestSpec, responseSpec); if (Objects.nonNull(requestSpec.getBody())) { MediaType mediaType = new MediaType(); mediaType.setSchema(new Schema()); //Ignore ClassCastException for https://github.com/rest-assured/rest-assured/issues/1232 try { requestSpec.getFormParams().forEach((n, v) -> mediaType.getSchema().addProperties(n, new Schema().example(v))); } catch (ClassCastException ex) { requestSpec.getFormParams().keySet().forEach((n -> mediaType.getSchema().addProperties(n, new Schema()))); } requestSpec.getMultiPartParams().forEach(multiPartSpecification -> mediaType.getSchema().addProperties(multiPartSpecification.getControlName(), new Schema())); operation.requestBody( new RequestBody().content(new Content().addMediaType(requestSpec.getContentType(), mediaType))); } operation.responses(new ApiResponses() .addApiResponse(valueOf(response.statusCode()), new ApiResponse().content(new Content().addMediaType(response.getContentType(), new MediaType())))); PathItem pathItem = new PathItem(); pathItem.operation(PathItem.HttpMethod.valueOf(requestSpec.getMethod().toUpperCase()), operation); OpenAPI openAPI = new OpenAPI() .addServersItem(new Server().url(URI.create(requestSpec.getURI()).getHost())) .path(requestSpec.getUserDefinedPath(), pathItem); writer.write(openAPI); return response; } } ================================================ FILE: swagger-coverage-rest-assured/src/test/java/com/github/viclovsky/swagger/coverage/RequestLoggerFilterTest.java ================================================ package com.github.viclovsky.swagger.coverage; import com.github.tomakehurst.wiremock.junit.WireMockRule; import io.restassured.RestAssured; import io.restassured.http.Header; import org.apache.http.HttpStatus; import org.hamcrest.io.FileMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.OUTPUT_DIRECTORY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.iterableWithSize; public class RequestLoggerFilterTest { private static final String BODY_STRING = "Hello world!"; @Rule public WireMockRule mock = new WireMockRule(options().dynamicPort()); @Rule public TemporaryFolder folder = new TemporaryFolder(); private List getPaths(Path path) { try (Stream paths = Files.walk(path)) { return paths.filter(Files::isRegularFile).collect(Collectors.toList()); } catch (IOException e) { throw new RuntimeException("can'n walk files in swagger output directory"); } } @Before public void setUp() { configureFor(mock.port()); stubFor(get(anyUrl()) .willReturn(aResponse().withStatus(HttpStatus.SC_OK) .withBody(BODY_STRING))); } @Test public void shouldCreateDefaultOutputFolder() { RestAssured.given().filter(new SwaggerCoverageRestAssured()) .get(mock.url("/hello")); assertThat(Paths.get(OUTPUT_DIRECTORY).toFile(), FileMatchers.anExistingDirectory()); } @Test public void shouldDumpSwaggerFile() throws IOException { Path output = folder.newFolder().toPath(); RestAssured.given().filter(new SwaggerCoverageRestAssured(new FileSystemOutputWriter(output))) .multiPart("file", "{}") .header(new Header("X-Request-ID", "h")) .formParam("form_param", "f") .queryParam("query_param", "q") .pathParam("path_param", "p") .get(mock.url("/hello/{path_param}")); assertThat(getPaths(output), iterableWithSize(1)); } @Test public void shouldCatchExceptionRestAssuredIssue1232() throws IOException { Path output = folder.newFolder().toPath(); RestAssured.given().filter(new SwaggerCoverageRestAssured(new FileSystemOutputWriter(output))) .multiPart("file", "{}") .header(new Header("X-Request-ID", "h")) .formParam("form_param", "f", "f2") .queryParam("query_param", "q", "q2") .pathParam("path_param", "p") .get(mock.url("/hello/{path_param}")); assertThat(getPaths(output), iterableWithSize(1)); } } ================================================ FILE: swagger-coverage-rest-assured/src/test/java/com/github/viclovsky/swagger/coverage/RequestLoggerV3FilterTest.java ================================================ package com.github.viclovsky.swagger.coverage; import com.github.tomakehurst.wiremock.junit.WireMockRule; import io.restassured.RestAssured; import io.restassured.http.Header; import org.apache.http.HttpStatus; import org.hamcrest.io.FileMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static com.github.viclovsky.swagger.coverage.SwaggerCoverageConstants.OUTPUT_DIRECTORY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.iterableWithSize; public class RequestLoggerV3FilterTest { private static final String BODY_STRING = "Hello world!"; @Rule public WireMockRule mock = new WireMockRule(options().dynamicPort()); @Rule public TemporaryFolder folder = new TemporaryFolder(); private List getPaths(Path path) { try (Stream paths = Files.walk(path)) { return paths.filter(Files::isRegularFile).collect(Collectors.toList()); } catch (IOException e) { throw new RuntimeException("can'n walk files in swagger output directory"); } } @Before public void setUp() { configureFor(mock.port()); stubFor(get(anyUrl()) .willReturn(aResponse().withStatus(HttpStatus.SC_OK) .withBody(BODY_STRING))); } @Test public void shouldCreateDefaultOutputFolder() { RestAssured.given().filter(new SwaggerCoverageV3RestAssured()) .get(mock.url("/hello")); assertThat(Paths.get(OUTPUT_DIRECTORY).toFile(), FileMatchers.anExistingDirectory()); } @Test public void shouldDumpSwaggerFile() throws IOException { Path output = folder.newFolder().toPath(); RestAssured.given().filter(new SwaggerCoverageV3RestAssured(new FileSystemOutputWriter(output))) .multiPart("file", "{}") .header(new Header("X-Request-ID", "h")) .formParam("form_param", "f") .queryParam("query_param", "q") .pathParam("path_param", "p") .get(mock.url("/hello/{path_param}")); assertThat(getPaths(output), iterableWithSize(1)); } @Test public void shouldCatchExceptionRestAssuredIssue1232() throws IOException { Path output = folder.newFolder().toPath(); RestAssured.given().filter(new SwaggerCoverageV3RestAssured(new FileSystemOutputWriter(output))) .multiPart("file", "{}") .header(new Header("X-Request-ID", "h")) .formParam("form_param", "f", "f2") .queryParam("query_param", "q", "q2") .pathParam("path_param", "p") .get(mock.url("/hello/{path_param}")); assertThat(getPaths(output), iterableWithSize(1)); } }