Repository: springdoc/springdoc-openapi-demos Branch: master Commit: c95efaf3dc48 Files: 238 Total size: 382.0 KB Directory structure: gitextract_h9gh0xwv/ ├── .gitattributes ├── .gitignore ├── Jenkinsfile ├── LICENSE ├── README.md ├── SUPPORT.md ├── demo-book-service/ │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── book/ │ │ │ ├── SpringdocApplication.java │ │ │ ├── controller/ │ │ │ │ └── BookController.java │ │ │ ├── exception/ │ │ │ │ ├── BookNotFoundException.java │ │ │ │ └── GlobalControllerExceptionHandler.java │ │ │ ├── model/ │ │ │ │ └── Book.java │ │ │ └── repository/ │ │ │ └── BookRepository.java │ │ └── resources/ │ │ ├── application.yml │ │ └── logback.xml │ └── test/ │ └── java/ │ └── org/ │ └── springdoc/ │ └── demo/ │ └── services/ │ └── book/ │ └── SwaggerUnitTest.java ├── demo-microservices/ │ ├── .gitignore │ ├── README.md │ ├── config-service/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── config/ │ │ │ └── ConfigApplication.java │ │ └── resources/ │ │ ├── application.yml │ │ └── config/ │ │ ├── department-service.yml │ │ ├── discovery-service.yml │ │ ├── employee-service.yml │ │ ├── gateway-service.yml │ │ └── organization-service.yml │ ├── department-service/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── department/ │ │ │ ├── DepartmentApplication.java │ │ │ ├── client/ │ │ │ │ └── EmployeeClient.java │ │ │ ├── controller/ │ │ │ │ └── DepartmentController.java │ │ │ ├── model/ │ │ │ │ ├── Department.java │ │ │ │ └── Employee.java │ │ │ └── repository/ │ │ │ └── DepartmentRepository.java │ │ └── resources/ │ │ └── application.yml │ ├── discovery-service/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── discovery/ │ │ │ └── DiscoveryApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── employee-service/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── employee/ │ │ │ ├── EmployeeApplication.java │ │ │ ├── controller/ │ │ │ │ └── EmployeeController.java │ │ │ ├── model/ │ │ │ │ └── Employee.java │ │ │ └── repository/ │ │ │ └── EmployeeRepository.java │ │ └── resources/ │ │ └── application.yml │ ├── gateway-service/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── gateway/ │ │ │ ├── ContextPathRewritePathGatewayFilterFactory.java │ │ │ └── GatewayApplication.java │ │ └── resources/ │ │ └── application.yml │ ├── organization-service/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── organization/ │ │ │ ├── OrganizationApplication.java │ │ │ ├── client/ │ │ │ │ ├── DepartmentClient.java │ │ │ │ └── EmployeeClient.java │ │ │ ├── controller/ │ │ │ │ └── OrganizationController.java │ │ │ ├── model/ │ │ │ │ ├── Department.java │ │ │ │ ├── Employee.java │ │ │ │ └── Organization.java │ │ │ └── repository/ │ │ │ └── OrganizationRepository.java │ │ └── resources/ │ │ └── application.yml │ └── pom.xml ├── demo-oauth2/ │ ├── .gitignore │ ├── README.md │ ├── oauth-authorization-server/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── auth/ │ │ │ ├── AuthorizationServerApp.java │ │ │ ├── DefaultSecurityConfig.java │ │ │ ├── Jwks.java │ │ │ ├── KeyGeneratorUtils.java │ │ │ └── SecurityConfig.java │ │ └── resources/ │ │ └── application.yml │ ├── oauth-resource-server-webflux/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── resource/ │ │ │ ├── ResourceServerApp.java │ │ │ ├── config/ │ │ │ │ ├── OpenApiConfig.java │ │ │ │ └── SecurityConfig.java │ │ │ ├── model/ │ │ │ │ └── Foo.java │ │ │ ├── repository/ │ │ │ │ └── IFooRepository.java │ │ │ ├── service/ │ │ │ │ ├── IFooService.java │ │ │ │ └── impl/ │ │ │ │ └── FooServiceImpl.java │ │ │ └── web/ │ │ │ ├── controller/ │ │ │ │ ├── FooController.java │ │ │ │ └── UserInfoController.java │ │ │ └── dto/ │ │ │ └── FooDTO.java │ │ └── resources/ │ │ ├── application.yml │ │ └── data.sql │ ├── oauth-resource-server-webmvc/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── resource/ │ │ │ ├── ResourceServerApp.java │ │ │ ├── config/ │ │ │ │ ├── OpenApiConfig.java │ │ │ │ └── SecurityConfig.java │ │ │ ├── model/ │ │ │ │ └── Foo.java │ │ │ ├── repository/ │ │ │ │ └── IFooRepository.java │ │ │ ├── service/ │ │ │ │ ├── IFooService.java │ │ │ │ └── impl/ │ │ │ │ └── FooServiceImpl.java │ │ │ └── web/ │ │ │ ├── controller/ │ │ │ │ ├── FooController.java │ │ │ │ └── UserInfoController.java │ │ │ └── dto/ │ │ │ └── FooDTO.java │ │ └── resources/ │ │ ├── application.yml │ │ └── data.sql │ └── pom.xml ├── demo-person-service/ │ ├── .gitattributes │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── services/ │ │ └── person/ │ │ ├── SampleApplication.java │ │ ├── config/ │ │ │ └── SampleConfig.java │ │ ├── controller/ │ │ │ └── PersonController.java │ │ ├── exceptions/ │ │ │ ├── ErrorMessage.java │ │ │ ├── GlobalControllerAdvice.java │ │ │ └── Problem.java │ │ └── model/ │ │ └── Person.java │ └── resources/ │ └── application.properties ├── demo-spring-boot-3-webflux/ │ ├── .gitignore │ ├── .java-version │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── app3/ │ │ ├── WebfluxDemoApplication.java │ │ ├── controller/ │ │ │ ├── ExceptionTranslator.java │ │ │ ├── TweetController.java │ │ │ └── TweetMapper.java │ │ ├── dto/ │ │ │ └── TweetDTO.java │ │ ├── exception/ │ │ │ └── TweetNotFoundException.java │ │ ├── model/ │ │ │ └── Tweet.java │ │ ├── payload/ │ │ │ └── ErrorResponse.java │ │ └── repository/ │ │ └── TweetRepository.java │ └── resources/ │ ├── META-INF/ │ │ └── native-image/ │ │ └── reflect-config.json │ └── application.yml ├── demo-spring-boot-3-webflux-functional/ │ ├── .gitignore │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── app4/ │ │ ├── AppNativeConfiguration.java │ │ ├── WebfluxFunctionalDemoApplication.java │ │ ├── coffee/ │ │ │ ├── Coffee.java │ │ │ ├── CoffeeOrder.java │ │ │ ├── CoffeeRepository.java │ │ │ ├── CoffeeService.java │ │ │ └── RouteConfig.java │ │ ├── employee/ │ │ │ ├── Employee.java │ │ │ ├── EmployeeFunctionalConfig.java │ │ │ └── EmployeeRepository.java │ │ └── user/ │ │ ├── RoutingConfiguration.java │ │ ├── User.java │ │ ├── UserHandler.java │ │ ├── UserRepository.java │ │ └── UserRepositoryImpl.java │ └── resources/ │ ├── application.yml │ ├── logback-spring.xml │ └── schema.sql ├── demo-spring-boot-3-webmvc/ │ ├── .gitignore │ ├── .java-version │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── app2/ │ │ ├── Application.java │ │ ├── api/ │ │ │ ├── ApiUtil.java │ │ │ ├── ExceptionTranslator.java │ │ │ ├── PetApi.java │ │ │ ├── PetApiController.java │ │ │ ├── PetApiDelegate.java │ │ │ ├── PetApiDelegateImpl.java │ │ │ ├── StoreApi.java │ │ │ ├── StoreApiController.java │ │ │ ├── StoreApiDelegate.java │ │ │ ├── StoreApiDelegateImpl.java │ │ │ ├── UserApi.java │ │ │ ├── UserApiController.java │ │ │ ├── UserApiDelegate.java │ │ │ └── UserApiDelegateImpl.java │ │ ├── model/ │ │ │ ├── Body.java │ │ │ ├── Body1.java │ │ │ ├── Category.java │ │ │ ├── ModelApiResponse.java │ │ │ ├── Order.java │ │ │ ├── Pet.java │ │ │ ├── Tag.java │ │ │ └── User.java │ │ └── repository/ │ │ ├── HashMapRepository.java │ │ ├── OrderRepository.java │ │ ├── PetRepository.java │ │ └── UserRepository.java │ └── resources/ │ ├── application.yml │ └── petstore.yml ├── demo-spring-boot-webflux-scalar/ │ ├── pom.xml │ ├── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── app3/ │ │ │ ├── WebfluxDemoApplication.java │ │ │ ├── controller/ │ │ │ │ ├── ExceptionTranslator.java │ │ │ │ ├── TweetController.java │ │ │ │ └── TweetMapper.java │ │ │ ├── dto/ │ │ │ │ └── TweetDTO.java │ │ │ ├── exception/ │ │ │ │ └── TweetNotFoundException.java │ │ │ ├── model/ │ │ │ │ └── Tweet.java │ │ │ ├── payload/ │ │ │ │ └── ErrorResponse.java │ │ │ └── repository/ │ │ │ └── TweetRepository.java │ │ └── resources/ │ │ └── application.yml │ └── target/ │ └── maven-status/ │ └── maven-compiler-plugin/ │ └── compile/ │ └── default-compile/ │ ├── createdFiles.lst │ └── inputFiles.lst ├── demo-spring-boot-webmvc-scalar/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── services/ │ │ └── book/ │ │ ├── SpringdocApplication.java │ │ ├── controller/ │ │ │ └── BookController.java │ │ ├── exception/ │ │ │ ├── BookNotFoundException.java │ │ │ └── GlobalControllerExceptionHandler.java │ │ ├── model/ │ │ │ └── Book.java │ │ └── repository/ │ │ └── BookRepository.java │ └── resources/ │ ├── application.yml │ └── logback.xml ├── demo-spring-cloud-function/ │ ├── .gitignore │ ├── pom.xml │ ├── spring-cloud-function-webflux/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── springdoc/ │ │ │ └── demo/ │ │ │ └── services/ │ │ │ └── functions/ │ │ │ ├── PersonDTO.java │ │ │ └── SampleApplication.java │ │ └── resources/ │ │ └── application.yml │ └── spring-cloud-function-webmvc/ │ ├── .gitignore │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── services/ │ │ └── functions/ │ │ ├── PersonDTO.java │ │ └── SampleApplication.java │ └── resources/ │ └── application.yml ├── demo-spring-data-rest/ │ ├── .gitignore │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── data/ │ │ └── rest/ │ │ ├── Account.java │ │ ├── AccountRepository.java │ │ ├── Customer.java │ │ ├── CustomerRepository.java │ │ └── SpringdocApplication.java │ └── resources/ │ └── application.properties ├── demo-spring-hateoas/ │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── springdoc/ │ │ └── demo/ │ │ └── services/ │ │ └── hateoas/ │ │ ├── DatabaseLoader.java │ │ ├── Employee.java │ │ ├── EmployeeController.java │ │ ├── EmployeeRepository.java │ │ └── SpringdocApplication.java │ └── resources/ │ └── application.properties ├── pom.xml └── settings.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # # https://help.github.com/articles/dealing-with-line-endings/ # # These are explicitly windows files and should use crlf *.bat text eol=crlf ================================================ FILE: .gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache # Ignore Gradle project-specific cache directory .gradle # Ignore Gradle build output directory build ================================================ FILE: Jenkinsfile ================================================ #!/usr/bin/env groovy node { stage('checkout') { deleteDir() checkout scm } stage('Clean') { withMaven(jdk: 'java-17', maven: 'maven-3.8.4'){ sh "mvn clean -T100" } } stage('Package') { withMaven(jdk: 'java-17', maven: 'maven-3.8.4'){ sh "mvn -Pjib package jib:build -T100" } } stage("Deploy") { build 'springdoc-openapi-demos-v2-deploy' deleteDir() } } ================================================ 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 [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 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 ================================================ [![Build Status](https://ci-cd.springdoc.org:8443/buildStatus/icon?job=springdoc-openapi-demos%2Fmaster)](https://ci-cd.springdoc.org:8443/view/springdoc-openapi-demos/job/springdoc-openapi-demos/job/master/) # Spring-boot with OpenAPI Demo applications. ## [Demo Spring Boot 3 Web MVC with OpenAPI 3](https://demos.springdoc.org/demo-spring-boot-3-webmvc). ## [Demo Spring Boot 3 WebFlux with OpenAPI 3](https://demos.springdoc.org/demo-spring-boot-3-webflux/swagger-ui.html). ## [Demo Spring Boot 3 Web MVC with OpenAPI 3 and Scalar](https://demos.springdoc.org/demo-spring-boot-webmvc-scalar/scalar). ## [Demo Spring Boot 3 WebFlux with OpenAPI 3 and Scalar](https://demos.springdoc.org/demo-spring-boot-webflux-scalar/scalar). ## [Demo Spring Boot 3 WebFlux with Functional endpoints OpenAPI 3](https://demos.springdoc.org/demo-spring-boot-3-webflux-functional/swagger-ui.html). ## [Demo Spring Boot 3 and Spring Cloud Function Web MVC](https://demos.springdoc.org/spring-cloud-function-webmvc). ## [Demo Spring Boot 3 and Spring Cloud Function WebFlux](https://demos.springdoc.org/spring-cloud-function-webflux/swagger-ui.html). ## [Demo Spring Boot 3 and Spring Cloud Gateway](https://demos.springdoc.org/demo-microservices/swagger-ui.html). ![Branching](https://springdoc.org/img/pets.png) # **Thank you for the support** * Thanks a lot [JetBrains](https://www.jetbrains.com/?from=springdoc-openapi) for supporting springdoc-openapi project. ![JenBrains logo](https://springdoc.org/img/jetbrains.svg) ================================================ FILE: SUPPORT.md ================================================ ## Where to get help If you think you've found a bug in The Architect Theme, please [check the existing issues](https://github.com/pages-themes/architect/issues), and if no one has reported the problem, [open a new issue](https://github.com/pages-themes/architect/issues/new). If you have a general question about the theme, how to implement it, or how to customize it for your site you have two options: 1. [Contact GitHub Support](https://github.com/contact?form%5Bsubject%5D=GitHub%20Pages%20theme%20pages-themes/architect) , or 2. Ask your question of the Jekyll community on [talk.jekyllrb.com](https://talk.jekyllrb.com/) ================================================ FILE: demo-book-service/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-book-service/README.md ================================================ ### Relevant Articles: - [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation) ================================================ FILE: demo-book-service/pom.xml ================================================ 4.0.0 org.springdoc springdoc-openapi-demos 3.1.7-SNAPSHOT demo-book-service org.springframework.boot spring-boot-starter-web org.springdoc springdoc-openapi-starter-webmvc-ui com.github.therapi therapi-runtime-javadoc 0.15.0 org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin com.github.therapi therapi-runtime-javadoc-scribe 0.15.0 ================================================ FILE: demo-book-service/src/main/java/org/springdoc/demo/services/book/SpringdocApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringdocApplication { public static void main(String[] args) { SpringApplication.run(SpringdocApplication.class, args); } } ================================================ FILE: demo-book-service/src/main/java/org/springdoc/demo/services/book/controller/BookController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.controller; import java.util.Collection; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springdoc.demo.services.book.exception.BookNotFoundException; import org.springdoc.demo.services.book.model.Book; import org.springdoc.demo.services.book.repository.BookRepository; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; /** * The type Book controller. */ @RestController @RequestMapping("/api/book") public class BookController { /** * The Repository. */ private final BookRepository repository; /** * Instantiates a new Book controller. * * @param repository the repository */ public BookController(BookRepository repository) { this.repository = repository; } /** * Find by id book. * * @param id the id * @return the book */ @GetMapping("/{id}") public Book findById(@PathVariable long id) { return repository.findById(id) .orElseThrow(() -> new BookNotFoundException()); } /** * Find books collection. * * @return the collection */ @GetMapping("/") public Collection findBooks() { return repository.getBooks(); } /** * Update book book. * * @param id the id * @param book the book * @return the book */ @PutMapping("/{id}") @ResponseStatus(HttpStatus.OK) public Book updateBook(@PathVariable("id") final String id, @RequestBody final Book book) { return book; } /** * Patch book book. * * @param id the id * @param book the book * @return the book */ @PatchMapping("/{id}") @ResponseStatus(HttpStatus.OK) public Book patchBook(@PathVariable("id") final String id, @RequestBody final Book book) { return book; } /** * Post book book. * * @param book the book * @return the book */ @PostMapping("/") @ResponseStatus(HttpStatus.CREATED) public Book postBook(@NotNull @Valid @RequestBody final Book book) { return book; } /** * Head book book. * * @return the book */ @RequestMapping(method = RequestMethod.HEAD, value = "/") @ResponseStatus(HttpStatus.OK) public Book headBook() { return new Book(); } /** * Delete book long. * * @param id the id * @return the long */ @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.OK) public long deleteBook(@PathVariable final long id) { return id; } } ================================================ FILE: demo-book-service/src/main/java/org/springdoc/demo/services/book/exception/BookNotFoundException.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.exception; @SuppressWarnings("serial") public class BookNotFoundException extends RuntimeException { public BookNotFoundException() { } } ================================================ FILE: demo-book-service/src/main/java/org/springdoc/demo/services/book/exception/GlobalControllerExceptionHandler.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.exception; import java.time.Instant; import org.springframework.core.convert.ConversionFailedException; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @RestControllerAdvice public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(ConversionFailedException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ProblemDetail handleConnversion(RuntimeException e) { ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.getMessage()); problemDetail.setTitle("Bookmark is Not Found"); problemDetail.setProperty("errorCategory", "Generic Exception"); problemDetail.setProperty("timestamp", Instant.now()); return problemDetail; } @ExceptionHandler(BookNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ProblemDetail handleBookNotFound(RuntimeException e) { ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage()); problemDetail.setTitle("Book Not Found"); problemDetail.setProperty("errorCategory", "Generic Exception"); problemDetail.setProperty("timestamp", Instant.now()); return problemDetail; } } ================================================ FILE: demo-book-service/src/main/java/org/springdoc/demo/services/book/model/Book.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.model; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; /** * The type Book. */ public class Book { /** * The Id. */ private long id; /** * The Title. */ @NotBlank @Size(min = 0, max = 20) private String title; /** * The Author. */ @NotBlank @Size(min = 0, max = 30) private String author; /** * Gets id. * * @return the id */ public long getId() { return id; } /** * Sets id. * * @param id the id */ public void setId(long id) { this.id = id; } /** * Gets title. * * @return the title */ public String getTitle() { return title; } /** * Sets title. * * @param title the title */ public void setTitle(String title) { this.title = title; } /** * Gets author. * * @return the author */ public String getAuthor() { return author; } /** * Sets author. * * @param author the author */ public void setAuthor(String author) { this.author = author; } } ================================================ FILE: demo-book-service/src/main/java/org/springdoc/demo/services/book/repository/BookRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.springdoc.demo.services.book.model.Book; import org.springframework.stereotype.Repository; @Repository public class BookRepository { private Map books = new HashMap<>(); public Optional findById(long id) { return Optional.ofNullable(books.get(id)); } public void add(Book book) { books.put(book.getId(), book); } public Collection getBooks() { return books.values(); } } ================================================ FILE: demo-book-service/src/main/resources/application.yml ================================================ server: forward-headers-strategy: framework springdoc: version: '@springdoc.version@' swagger-ui: use-root-path: true ================================================ FILE: demo-book-service/src/main/resources/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: demo-book-service/src/test/java/org/springdoc/demo/services/book/SwaggerUnitTest.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @AutoConfigureMockMvc @SpringBootTest public class SwaggerUnitTest { @Autowired protected MockMvc mockMvc; @Test public void shouldDisplaySwaggerUiPage() throws Exception { MvcResult mvcResult = mockMvc.perform(get("/swagger-ui/index.html")).andExpect(status().isOk()).andReturn(); String contentAsString = mvcResult.getResponse().getContentAsString(); Assertions.assertTrue(contentAsString.contains("Swagger UI")); } } ================================================ FILE: demo-microservices/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-microservices/README.md ================================================ ## sample microservices architecture using spring-cloud-gateway/eureka/spring-cloud-config ### Relevant information: * Swagger-ui : http://localhost:8060/swagger-ui.html * Eureka: http://localhost:8761/ ================================================ FILE: demo-microservices/config-service/.gitignore ================================================ /target/ /.classpath /.project /.settings/ ================================================ FILE: demo-microservices/config-service/pom.xml ================================================ 4.0.0 config-service org.springdoc demo-microservices 3.1.7-SNAPSHOT org.springframework.cloud spring-cloud-config-server ================================================ FILE: demo-microservices/config-service/src/main/java/org/springdoc/demo/services/config/ConfigApplication.java ================================================ package org.springdoc.demo.services.config; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { new SpringApplicationBuilder(ConfigApplication.class).run(args); } } ================================================ FILE: demo-microservices/config-service/src/main/resources/application.yml ================================================ server: port: 8088 spring: profiles: active: native ================================================ FILE: demo-microservices/config-service/src/main/resources/config/department-service.yml ================================================ server: port: 8090 forward-headers-strategy: framework eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka} instance: preferIpAddress: true logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n" springdoc: cache: disabled: true version: '@springdoc.version@' ================================================ FILE: demo-microservices/config-service/src/main/resources/config/discovery-service.yml ================================================ server: port: 8761 forward-headers-strategy: framework eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka} registerWithEureka: false fetchRegistry: false ================================================ FILE: demo-microservices/config-service/src/main/resources/config/employee-service.yml ================================================ server: port: 8092 forward-headers-strategy: framework eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka} instance: preferIpAddress: true logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n" springdoc: version: '@springdoc.version@' cache: disabled: true ================================================ FILE: demo-microservices/config-service/src/main/resources/config/gateway-service.yml ================================================ server: port: 8060 forward-headers-strategy: framework eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka} instance: preferIpAddress: true logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n" level: org.springframework.cloud.gateway: TRACE org.springframework.http.server.reactive: TRACE spring: cloud: gateway: server: webflux: trusted-proxies: "192\\.168\\.0\\..*|10\\.88\\.0\\..*|127\\.0\\.0\\.1|144\\.24\\.171\\.248" httpclient: ssl: useInsecureTrustManager: true discovery: locator: enabled: true routes: - id: employee-service uri: lb://employee-service predicates: - Path=/employee/**, /demo-microservices/employee/** filters: - ContextPathRewritePath=/demo-microservices/employee/(?.*), /$\{path} - RewritePath=/employee/(?.*), /$\{path} - id: department-service uri: lb://department-service predicates: - Path=/department/**, /demo-microservices/department/** filters: - ContextPathRewritePath=/demo-microservices/department/(?.*), /$\{path} - RewritePath=/department/(?.*), /$\{path} - id: organization-service uri: lb://organization-service predicates: - Path=/organization/**, /demo-microservices/organization/** filters: - ContextPathRewritePath=/demo-microservices/organization/(?.*), /$\{path} - RewritePath=/organization/(?.*), /$\{path} - id: openapi-proxy uri: https://demos.springdoc.org predicates: - Path=/demo-microservices/v3/api-docs/** filters: - RewritePath=/demo-microservices/v3/api-docs/(?.*), /demo-microservices/$\{path}/v3/api-docs - id: openapi uri: http://localhost:${server.port} predicates: - Path=/v3/api-docs/** filters: - RewritePath=/v3/api-docs/(?.*), /$\{path}/v3/api-docs springdoc: cache: disabled: true version: '@springdoc.version@' swagger-ui: use-root-path: true ================================================ FILE: demo-microservices/config-service/src/main/resources/config/organization-service.yml ================================================ server: port: 8091 forward-headers-strategy: framework eureka: client: serviceUrl: defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka} instance: preferIpAddress: true logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n" springdoc: version: '@springdoc.version@' cache: disabled: true ================================================ FILE: demo-microservices/department-service/.gitignore ================================================ /target/ /.classpath /.project /.settings/ ================================================ FILE: demo-microservices/department-service/pom.xml ================================================ 4.0.0 org.springdoc demo-microservices 3.1.7-SNAPSHOT department-service org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-openfeign-core org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springdoc springdoc-openapi-starter-webmvc-api ================================================ FILE: demo-microservices/department-service/src/main/java/org/springdoc/demo/services/department/DepartmentApplication.java ================================================ package org.springdoc.demo.services.department; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springdoc.demo.services.department.model.Department; import org.springdoc.demo.services.department.repository.DepartmentRepository; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @OpenAPIDefinition(info = @Info(title = "Department API", version = "${springdoc.version}", description = "Documentation Department API v1.0") ) public class DepartmentApplication { public static void main(String[] args) { SpringApplication.run(DepartmentApplication.class, args); } @Bean DepartmentRepository repository() { DepartmentRepository repository = new DepartmentRepository(); repository.add(new Department(1L, "Development")); repository.add(new Department(1L, "Operations")); repository.add(new Department(2L, "Development")); repository.add(new Department(2L, "Operations")); return repository; } } ================================================ FILE: demo-microservices/department-service/src/main/java/org/springdoc/demo/services/department/client/EmployeeClient.java ================================================ package org.springdoc.demo.services.department.client; import java.util.List; import org.springdoc.demo.services.department.model.Employee; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(name = "employee-service") public interface EmployeeClient { @GetMapping("/department/{departmentId}") List findByDepartment(@PathVariable("departmentId") Long departmentId); } ================================================ FILE: demo-microservices/department-service/src/main/java/org/springdoc/demo/services/department/controller/DepartmentController.java ================================================ package org.springdoc.demo.services.department.controller; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.demo.services.department.client.EmployeeClient; import org.springdoc.demo.services.department.model.Department; import org.springdoc.demo.services.department.repository.DepartmentRepository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class DepartmentController { private static final Logger LOGGER = LoggerFactory.getLogger(DepartmentController.class); private DepartmentRepository repository; private EmployeeClient employeeClient; public DepartmentController(DepartmentRepository repository, EmployeeClient employeeClient) { this.repository = repository; this.employeeClient = employeeClient; } @PostMapping("/") public Department add(@RequestBody Department department) { LOGGER.info("Department add: {}", department); return repository.add(department); } @GetMapping("/{id}") public Department findById(@PathVariable("id") Long id) { LOGGER.info("Department find: id={}", id); return repository.findById(id); } @GetMapping("/") public List findAll() { LOGGER.info("Department find"); return repository.findAll(); } @GetMapping("/organization/{organizationId}") public List findByOrganization(@PathVariable("organizationId") Long organizationId) { LOGGER.info("Department find: organizationId={}", organizationId); return repository.findByOrganization(organizationId); } @GetMapping("/organization/{organizationId}/with-employees") public List findByOrganizationWithEmployees(@PathVariable("organizationId") Long organizationId) { LOGGER.info("Department find: organizationId={}", organizationId); List departments = repository.findByOrganization(organizationId); departments.forEach(d -> d.setEmployees(employeeClient.findByDepartment(d.getId()))); return departments; } } ================================================ FILE: demo-microservices/department-service/src/main/java/org/springdoc/demo/services/department/model/Department.java ================================================ package org.springdoc.demo.services.department.model; import java.util.ArrayList; import java.util.List; public class Department { private Long id; private Long organizationId; private String name; private List employees = new ArrayList<>(); public Department() { } public Department(Long organizationId, String name) { super(); this.organizationId = organizationId; this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getOrganizationId() { return organizationId; } public void setOrganizationId(Long organizationId) { this.organizationId = organizationId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getEmployees() { return employees; } public void setEmployees(List employees) { this.employees = employees; } @Override public String toString() { return "Department [id=" + id + ", organizationId=" + organizationId + ", name=" + name + "]"; } } ================================================ FILE: demo-microservices/department-service/src/main/java/org/springdoc/demo/services/department/model/Employee.java ================================================ package org.springdoc.demo.services.department.model; public class Employee { private Long id; private String name; private int age; private String position; public Employee() { } public Employee(String name, int age, String position) { this.name = name; this.age = age; this.position = position; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", position=" + position + "]"; } } ================================================ FILE: demo-microservices/department-service/src/main/java/org/springdoc/demo/services/department/repository/DepartmentRepository.java ================================================ package org.springdoc.demo.services.department.repository; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.springdoc.demo.services.department.model.Department; public class DepartmentRepository { private List departments = new ArrayList<>(); public Department add(Department department) { department.setId((long) (departments.size() + 1)); departments.add(department); return department; } public Department findById(Long id) { Optional department = departments.stream().filter(a -> a.getId().equals(id)).findFirst(); if (department.isPresent()) return department.get(); else return null; } public List findAll() { return departments; } public List findByOrganization(Long organizationId) { return departments.stream().filter(a -> a.getOrganizationId().equals(organizationId)).collect(Collectors.toList()); } } ================================================ FILE: demo-microservices/department-service/src/main/resources/application.yml ================================================ spring: application: name: department-service config: import: "optional:configserver:${CONFIG_SERVER:http://localhost:8088}" ================================================ FILE: demo-microservices/discovery-service/.gitignore ================================================ /target/ /.settings/ /.classpath /.project ================================================ FILE: demo-microservices/discovery-service/pom.xml ================================================ 4.0.0 org.springdoc demo-microservices 3.1.7-SNAPSHOT discovery-service org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.cloud spring-cloud-starter-config ================================================ FILE: demo-microservices/discovery-service/src/main/java/org/springdoc/demo/services/discovery/DiscoveryApplication.java ================================================ package org.springdoc.demo.services.discovery; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class DiscoveryApplication { public static void main(String[] args) { new SpringApplicationBuilder(DiscoveryApplication.class).run(args); } } ================================================ FILE: demo-microservices/discovery-service/src/main/resources/application.yml ================================================ spring: application: name: discovery-service config: import: "optional:configserver:${CONFIG_SERVER:http://localhost:8088}" ================================================ FILE: demo-microservices/employee-service/.gitignore ================================================ /target/ /.classpath /.project /.settings/ ================================================ FILE: demo-microservices/employee-service/pom.xml ================================================ 4.0.0 org.springdoc demo-microservices 3.1.7-SNAPSHOT employee-service org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-config org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springdoc springdoc-openapi-starter-webmvc-api ================================================ FILE: demo-microservices/employee-service/src/main/java/org/springdoc/demo/services/employee/EmployeeApplication.java ================================================ package org.springdoc.demo.services.employee; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springdoc.demo.services.employee.model.Employee; import org.springdoc.demo.services.employee.repository.EmployeeRepository; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableDiscoveryClient @OpenAPIDefinition(info = @Info(title = "Employee API", version = "${springdoc.version}", description = "Documentation Employee API v1.0") ) public class EmployeeApplication { public static void main(String[] args) { SpringApplication.run(EmployeeApplication.class, args); } @Bean EmployeeRepository repository() { EmployeeRepository repository = new EmployeeRepository(); repository.add(new Employee(1L, 1L, "John Smith", 34, "Analyst")); repository.add(new Employee(1L, 1L, "Darren Hamilton", 37, "Manager")); repository.add(new Employee(1L, 1L, "Tom Scott", 26, "Developer")); repository.add(new Employee(1L, 2L, "Anna London", 39, "Analyst")); repository.add(new Employee(1L, 2L, "Patrick Dempsey", 27, "Developer")); repository.add(new Employee(2L, 3L, "Kevin Price", 38, "Developer")); repository.add(new Employee(2L, 3L, "Ian Scott", 34, "Developer")); repository.add(new Employee(2L, 3L, "Andrew Campton", 30, "Manager")); repository.add(new Employee(2L, 4L, "Steve Franklin", 25, "Developer")); repository.add(new Employee(2L, 4L, "Elisabeth Smith", 30, "Developer")); return repository; } } ================================================ FILE: demo-microservices/employee-service/src/main/java/org/springdoc/demo/services/employee/controller/EmployeeController.java ================================================ package org.springdoc.demo.services.employee.controller; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.demo.services.employee.model.Employee; import org.springdoc.demo.services.employee.repository.EmployeeRepository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class EmployeeController { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeController.class); private EmployeeRepository repository; public EmployeeController(EmployeeRepository repository) { this.repository = repository; } @PostMapping("/") public Employee add(@RequestBody Employee employee) { LOGGER.info("Employee add: {}", employee); return repository.add(employee); } @GetMapping("/{id}") public Employee findById(@PathVariable("id") Long id) { LOGGER.info("Employee find: id={}", id); return repository.findById(id); } @GetMapping("/") public List findAll() { LOGGER.info("Employee find"); return repository.findAll(); } @GetMapping("/department/{departmentId}") public List findByDepartment(@PathVariable("departmentId") Long departmentId) { LOGGER.info("Employee find: departmentId={}", departmentId); return repository.findByDepartment(departmentId); } @GetMapping("/organization/{organizationId}") public List findByOrganization(@PathVariable("organizationId") Long organizationId) { LOGGER.info("Employee find: organizationId={}", organizationId); return repository.findByOrganization(organizationId); } } ================================================ FILE: demo-microservices/employee-service/src/main/java/org/springdoc/demo/services/employee/model/Employee.java ================================================ package org.springdoc.demo.services.employee.model; public class Employee { private Long id; private Long organizationId; private Long departmentId; private String name; private int age; private String position; public Employee() { } public Employee(Long organizationId, Long departmentId, String name, int age, String position) { this.organizationId = organizationId; this.departmentId = departmentId; this.name = name; this.age = age; this.position = position; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getOrganizationId() { return organizationId; } public void setOrganizationId(Long organizationId) { this.organizationId = organizationId; } public Long getDepartmentId() { return departmentId; } public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } @Override public String toString() { return "Employee [id=" + id + ", organizationId=" + organizationId + ", departmentId=" + departmentId + ", name=" + name + ", position=" + position + "]"; } } ================================================ FILE: demo-microservices/employee-service/src/main/java/org/springdoc/demo/services/employee/repository/EmployeeRepository.java ================================================ package org.springdoc.demo.services.employee.repository; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.springdoc.demo.services.employee.model.Employee; public class EmployeeRepository { private List employees = new ArrayList<>(); public Employee add(Employee employee) { employee.setId((long) (employees.size() + 1)); employees.add(employee); return employee; } public Employee findById(Long id) { Optional employee = employees.stream().filter(a -> a.getId().equals(id)).findFirst(); if (employee.isPresent()) return employee.get(); else return null; } public List findAll() { return employees; } public List findByDepartment(Long departmentId) { return employees.stream().filter(a -> a.getDepartmentId().equals(departmentId)).collect(Collectors.toList()); } public List findByOrganization(Long organizationId) { return employees.stream().filter(a -> a.getOrganizationId().equals(organizationId)).collect(Collectors.toList()); } } ================================================ FILE: demo-microservices/employee-service/src/main/resources/application.yml ================================================ spring: application: name: employee-service config: import: "optional:configserver:${CONFIG_SERVER:http://localhost:8088}" ================================================ FILE: demo-microservices/gateway-service/.gitignore ================================================ /target/ /.classpath /.project /.settings/ ================================================ FILE: demo-microservices/gateway-service/pom.xml ================================================ 4.0.0 org.springdoc demo-microservices 3.1.7-SNAPSHOT gateway-service org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-config org.springdoc springdoc-openapi-starter-webflux-ui ================================================ FILE: demo-microservices/gateway-service/src/main/java/org/springdoc/demo/services/gateway/ContextPathRewritePathGatewayFilterFactory.java ================================================ package org.springdoc.demo.services.gateway; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl; /** * @author bnasslahsen */ @Component public class ContextPathRewritePathGatewayFilterFactory extends RewritePathGatewayFilterFactory { @Override public GatewayFilter apply(Config config) { String replacement = config.getReplacement().replace("$\\", "$"); return (exchange, chain) -> { ServerHttpRequest req = exchange.getRequest(); addOriginalRequestUrl(exchange, req.getURI()); String path = req.getURI().getRawPath(); String newPath = path.replaceAll(config.getRegexp(), replacement); ServerHttpRequest request = req.mutate().path(newPath).contextPath("/").build(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI()); return chain.filter(exchange.mutate().request(request).build()); }; } } ================================================ FILE: demo-microservices/gateway-service/src/main/java/org/springdoc/demo/services/gateway/GatewayApplication.java ================================================ package org.springdoc.demo.services.gateway; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springdoc.core.properties.AbstractSwaggerUiConfigProperties.SwaggerUrl; import org.springdoc.core.properties.SwaggerUiConfigProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Lazy; import static org.springdoc.core.utils.Constants.DEFAULT_API_DOCS_URL; @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } @Bean @Lazy(false) public Set apis(RouteDefinitionLocator locator, SwaggerUiConfigProperties swaggerUiConfigProperties) { Set urls = new HashSet<>(); List definitions = locator.getRouteDefinitions().collectList().block(); definitions.stream().filter(routeDefinition -> routeDefinition.getId().matches(".*-service")).forEach(routeDefinition -> { String name = routeDefinition.getId().replaceAll("-service", ""); SwaggerUrl swaggerUrl = new SwaggerUrl(name, DEFAULT_API_DOCS_URL+"/" + name, null); urls.add(swaggerUrl); }); swaggerUiConfigProperties.setUrls(urls); return urls; } } ================================================ FILE: demo-microservices/gateway-service/src/main/resources/application.yml ================================================ spring: application: name: gateway-service config: import: "optional:configserver:${CONFIG_SERVER:http://localhost:8088}" ================================================ FILE: demo-microservices/organization-service/.gitignore ================================================ /target/ /.classpath /.project /.settings/ ================================================ FILE: demo-microservices/organization-service/pom.xml ================================================ 4.0.0 org.springdoc demo-microservices 3.1.7-SNAPSHOT organization-service org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-openfeign-core org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springdoc springdoc-openapi-starter-webmvc-api ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/OrganizationApplication.java ================================================ package org.springdoc.demo.services.organization; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springdoc.demo.services.organization.model.Organization; import org.springdoc.demo.services.organization.repository.OrganizationRepository; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @OpenAPIDefinition(info = @Info(title = "Organization API", version = "${springdoc.version}", description = "Documentation Organization API v1.0") ) public class OrganizationApplication { public static void main(String[] args) { SpringApplication.run(OrganizationApplication.class, args); } @Bean OrganizationRepository repository() { OrganizationRepository repository = new OrganizationRepository(); repository.add(new Organization("Microsoft", "Redmond, Washington, USA")); repository.add(new Organization("Oracle", "Redwood City, California, USA")); return repository; } } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/client/DepartmentClient.java ================================================ package org.springdoc.demo.services.organization.client; import java.util.List; import org.springdoc.demo.services.organization.model.Department; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(name = "department-service") public interface DepartmentClient { @GetMapping("/organization/{organizationId}") public List findByOrganization(@PathVariable("organizationId") Long organizationId); @GetMapping("/organization/{organizationId}/with-employees") public List findByOrganizationWithEmployees(@PathVariable("organizationId") Long organizationId); } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/client/EmployeeClient.java ================================================ package org.springdoc.demo.services.organization.client; import java.util.List; import org.springdoc.demo.services.organization.model.Employee; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(name = "employee-service") public interface EmployeeClient { @GetMapping("/organization/{organizationId}") List findByOrganization(@PathVariable("organizationId") Long organizationId); } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/controller/OrganizationController.java ================================================ package org.springdoc.demo.services.organization.controller; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.demo.services.organization.client.DepartmentClient; import org.springdoc.demo.services.organization.client.EmployeeClient; import org.springdoc.demo.services.organization.model.Organization; import org.springdoc.demo.services.organization.repository.OrganizationRepository; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class OrganizationController { private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationController.class); private OrganizationRepository repository; private DepartmentClient departmentClient; private EmployeeClient employeeClient; public OrganizationController(OrganizationRepository repository, DepartmentClient departmentClient, EmployeeClient employeeClient) { this.repository = repository; this.departmentClient = departmentClient; this.employeeClient = employeeClient; } @PostMapping public Organization add(@RequestBody Organization organization) { LOGGER.info("Organization add: {}", organization); return repository.add(organization); } @GetMapping public List findAll() { LOGGER.info("Organization find"); return repository.findAll(); } @GetMapping("/{id}") public Organization findById(@PathVariable("id") Long id) { LOGGER.info("Organization find: id={}", id); return repository.findById(id); } @GetMapping("/{id}/with-departments") public Organization findByIdWithDepartments(@PathVariable("id") Long id) { LOGGER.info("Organization find: id={}", id); Organization organization = repository.findById(id); organization.setDepartments(departmentClient.findByOrganization(organization.getId())); return organization; } @GetMapping("/{id}/with-departments-and-employees") public Organization findByIdWithDepartmentsAndEmployees(@PathVariable("id") Long id) { LOGGER.info("Organization find: id={}", id); Organization organization = repository.findById(id); organization.setDepartments(departmentClient.findByOrganizationWithEmployees(organization.getId())); return organization; } @GetMapping("/{id}/with-employees") public Organization findByIdWithEmployees(@PathVariable("id") Long id) { LOGGER.info("Organization find: id={}", id); Organization organization = repository.findById(id); organization.setEmployees(employeeClient.findByOrganization(organization.getId())); return organization; } } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/model/Department.java ================================================ package org.springdoc.demo.services.organization.model; import java.util.ArrayList; import java.util.List; public class Department { private Long id; private String name; private List employees = new ArrayList<>(); public Department() { } public Department(String name) { super(); this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getEmployees() { return employees; } public void setEmployees(List employees) { this.employees = employees; } @Override public String toString() { return "Department [id=" + id + ", name=" + name + "]"; } } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/model/Employee.java ================================================ package org.springdoc.demo.services.organization.model; public class Employee { private Long id; private String name; private int age; private String position; public Employee() { } public Employee(String name, int age, String position) { this.name = name; this.age = age; this.position = position; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", position=" + position + "]"; } } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/model/Organization.java ================================================ package org.springdoc.demo.services.organization.model; import java.util.ArrayList; import java.util.List; public class Organization { private Long id; private String name; private String address; private List departments = new ArrayList<>(); private List employees = new ArrayList<>(); public Organization() { } public Organization(String name, String address) { this.name = name; this.address = address; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public List getDepartments() { return departments; } public void setDepartments(List departments) { this.departments = departments; } public List getEmployees() { return employees; } public void setEmployees(List employees) { this.employees = employees; } @Override public String toString() { return "Organization [id=" + id + ", name=" + name + ", address=" + address + "]"; } } ================================================ FILE: demo-microservices/organization-service/src/main/java/org/springdoc/demo/services/organization/repository/OrganizationRepository.java ================================================ package org.springdoc.demo.services.organization.repository; import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.springdoc.demo.services.organization.model.Organization; public class OrganizationRepository { private List organizations = new ArrayList<>(); public Organization add(Organization organization) { organization.setId((long) (organizations.size() + 1)); organizations.add(organization); return organization; } public Organization findById(Long id) { Optional organization = organizations.stream().filter(a -> a.getId().equals(id)).findFirst(); if (organization.isPresent()) return organization.get(); else return null; } public List findAll() { return organizations; } } ================================================ FILE: demo-microservices/organization-service/src/main/resources/application.yml ================================================ spring: application: name: organization-service config: import: "optional:configserver:${CONFIG_SERVER:http://localhost:8088}" ================================================ FILE: demo-microservices/pom.xml ================================================ springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT pom 4.0.0 demo-microservices config-service discovery-service employee-service department-service organization-service gateway-service ================================================ FILE: demo-oauth2/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-oauth2/README.md ================================================ ## Spring Security OAuth - New Stack ### Relevant information: 1. `oauth-authorization-server` is a Keycloak Authorization Server wrapped as a Spring Boot application 2. There is one OAuth Client registered in the Authorization Server: 1. Client Id: newClient 2. Client secret: newClientSecret 3. Redirect Uris: - http://127.0.0.1:8081/resource-server/swagger-ui/oauth2-redirect.html - http://127.0.0.1:8082/resource-server/swagger-ui/oauth2-redirect.html 3. There is a test user registered in the Authorization Server: - josh@test.com / 123 4. `oauth-resource-server-webmvc` is a Spring Boot WebMVC based RESTFul API, acting as a backend Application swagger-ui: http://127.0.0.1:8081/resource-server/swagger-ui.html 5. `oauth-resource-server-webflux` is a Spring Boot WebFlux based RESTFul API, acting as a backend Application swagger-ui: http://127.0.0.1:8082/resource-server/swagger-ui.html ================================================ FILE: demo-oauth2/oauth-authorization-server/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-oauth2/oauth-authorization-server/pom.xml ================================================ 4.0.0 org.springdoc demo-oauth2 3.1.7-SNAPSHOT oauth-authorization-server org.springframework.security spring-security-oauth2-authorization-server org.springdoc springdoc-openapi-starter-webmvc-ui org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-jdbc com.h2database h2 ================================================ FILE: demo-oauth2/oauth-authorization-server/src/main/java/org/springdoc/demo/auth/AuthorizationServerApp.java ================================================ package org.springdoc.demo.auth; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AuthorizationServerApp { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApp.class, args); } } ================================================ FILE: demo-oauth2/oauth-authorization-server/src/main/java/org/springdoc/demo/auth/DefaultSecurityConfig.java ================================================ package org.springdoc.demo.auth; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @EnableWebSecurity @Configuration(proxyBeanMethods = false) public class DefaultSecurityConfig { @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz -> authz.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() .anyRequest().authenticated() ) .cors(withDefaults()) .formLogin(withDefaults()); return http.build(); } @Bean UserDetailsService users() { UserDetails user = User.withDefaultPasswordEncoder() .username("josh@test.com") .password("123") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } } ================================================ FILE: demo-oauth2/oauth-authorization-server/src/main/java/org/springdoc/demo/auth/Jwks.java ================================================ package org.springdoc.demo.auth; import java.security.KeyPair; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.UUID; import com.nimbusds.jose.jwk.RSAKey; public final class Jwks { private Jwks() { } public static RSAKey generateRsa() { KeyPair keyPair = KeyGeneratorUtils.generateRsaKey(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); return new RSAKey.Builder(publicKey) .privateKey(privateKey) .keyID(UUID.randomUUID().toString()) .build(); } } ================================================ FILE: demo-oauth2/oauth-authorization-server/src/main/java/org/springdoc/demo/auth/KeyGeneratorUtils.java ================================================ package org.springdoc.demo.auth; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.spec.ECFieldFp; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; import java.security.spec.EllipticCurve; final class KeyGeneratorUtils { private KeyGeneratorUtils() { } static KeyPair generateRsaKey() { KeyPair keyPair; try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); keyPair = keyPairGenerator.generateKeyPair(); } catch (Exception ex) { throw new IllegalStateException(ex); } return keyPair; } static KeyPair generateEcKey() { EllipticCurve ellipticCurve = new EllipticCurve( new ECFieldFp( new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")), new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"), new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291")); ECPoint ecPoint = new ECPoint( new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"), new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109")); ECParameterSpec ecParameterSpec = new ECParameterSpec( ellipticCurve, ecPoint, new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"), 1); KeyPair keyPair; try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); keyPairGenerator.initialize(ecParameterSpec); keyPair = keyPairGenerator.generateKeyPair(); } catch (Exception ex) { throw new IllegalStateException(ex); } return keyPair; } } ================================================ FILE: demo-oauth2/oauth-authorization-server/src/main/java/org/springdoc/demo/auth/SecurityConfig.java ================================================ package org.springdoc.demo.auth; import java.util.List; import java.util.UUID; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.oidc.OidcScopes; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService; import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import static org.springframework.security.config.Customizer.withDefaults; @Configuration(proxyBeanMethods = false) public class SecurityConfig { private static final List ALLOWED_HEADERS = List.of("Access-Control-Allow-Origin", "x-requested-with"); private static final List ALLOWED_METHODS = List.of("POST"); private static final List ALLOWED_ALL = List.of("http://127.0.0.1:8081", "http://127.0.0.1:8082", "http://144.24.171.248:8095","http://144.24.171.248:8096", "https://demos.springdoc.org"); @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(ALLOWED_ALL); configuration.setAllowedMethods(ALLOWED_METHODS); configuration.setAllowedHeaders(ALLOWED_HEADERS); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults()); http .cors(withDefaults()) .exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")) ) .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); return http.build(); } @Bean public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) { RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("newClient") .clientSecret("{noop}newClientSecret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .redirectUri("http://127.0.0.1:8081/resource-server/swagger-ui/oauth2-redirect.html") .redirectUri("http://127.0.0.1:8082/resource-server/swagger-ui/oauth2-redirect.html") .redirectUri("http://144.24.171.248:8095/resource-server/swagger-ui/oauth2-redirect.html") .redirectUri("http://144.24.171.248:8096/resource-server/swagger-ui/oauth2-redirect.html") .redirectUri("https://demos.springdoc.org/oauth-resource-server-webmvc/resource-server/swagger-ui/oauth2-redirect.html") .redirectUri("https://demos.springdoc.org/oauth-resource-server-webflux/resource-server/swagger-ui/oauth2-redirect.html") .scope(OidcScopes.OPENID) .scope(OidcScopes.PROFILE) .scope("springdoc.read") .scope("springdoc.write") .build(); // Save registered client in db as if in-memory JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate); registeredClientRepository.save(registeredClient); return registeredClientRepository; } @Bean public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); } @Bean public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository); } @Bean public JWKSource jwkSource() { RSAKey rsaKey = Jwks.generateRsa(); JWKSet jwkSet = new JWKSet(rsaKey); return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); } @Bean public JwtDecoder jwtDecoder(JWKSource jwkSource) { return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource); } @Bean public AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().build(); } @Bean public EmbeddedDatabase embeddedDatabase() { return new EmbeddedDatabaseBuilder() .generateUniqueName(true) .setType(EmbeddedDatabaseType.H2) .setScriptEncoding("UTF-8") .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql") .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") .build(); } } ================================================ FILE: demo-oauth2/oauth-authorization-server/src/main/resources/application.yml ================================================ spring: datasource: username: sa url: jdbc:h2:./data/keycloak;DB_CLOSE_ON_EXIT=FALSE hikari: maximum-pool-size: 25 minimum-idle: 1 server: forward-headers-strategy: native port: 8083 logging: level: root: INFO org.springframework.web: INFO org.springframework.security: INFO org.springframework.security.oauth2: INFO org.springframework.boot.autoconfigure: INFO springdoc: show-oauth2-endpoints: true ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/pom.xml ================================================ 4.0.0 org.springdoc demo-oauth2 3.1.7-SNAPSHOT oauth-resource-server-webflux org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-oauth2-resource-server org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 org.springdoc springdoc-openapi-starter-webflux-ui ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/ResourceServerApp.java ================================================ package org.springdoc.demo.resource; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class ResourceServerApp { public static void main(String[] args) throws Exception { SpringApplication.run(ResourceServerApp.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components()) .info(new Info().title("Foo API").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/config/OpenApiConfig.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.resource.config; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.security.OAuthFlow; import io.swagger.v3.oas.annotations.security.OAuthFlows; import io.swagger.v3.oas.annotations.security.OAuthScope; import io.swagger.v3.oas.annotations.security.SecurityScheme; @OpenAPIDefinition(info = @Info(title = "My App", description = "Some long and useful description", version = "v1")) @SecurityScheme(name = "security_auth", type = SecuritySchemeType.OAUTH2, flows = @OAuthFlows(authorizationCode = @OAuthFlow( authorizationUrl = "${springdoc.oAuthFlow.authorizationUrl}" , tokenUrl = "${springdoc.oAuthFlow.tokenUrl}", scopes = { @OAuthScope(name = "springdoc.read", description = "read scope"), @OAuthScope(name = "springdoc.write", description = "write scope") }))) public class OpenApiConfig {} ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/config/SecurityConfig.java ================================================ package org.springdoc.demo.resource.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration public class SecurityConfig { @Bean public SecurityWebFilterChain configure(ServerHttpSecurity http) { http .authorizeExchange().pathMatchers("/v3/api-docs/**", "/swagger-ui/**", "/webjars/**", "/swagger-ui.html").permitAll() .pathMatchers(HttpMethod.GET, "/user/info", "/api/foos/**") .hasAuthority("SCOPE_springdoc.read") .pathMatchers(HttpMethod.POST, "/api/foos") .hasAuthority("SCOPE_springdoc.write") .anyExchange().authenticated().and().oauth2ResourceServer().jwt(); return http.build(); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/model/Foo.java ================================================ package org.springdoc.demo.resource.model; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Foo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; protected Foo() { } public Foo(String name) { this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Foo other = (Foo) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "Foo [id=" + id + ", name=" + name + "]"; } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/repository/IFooRepository.java ================================================ package org.springdoc.demo.resource.repository; import org.springdoc.demo.resource.model.Foo; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; public interface IFooRepository extends PagingAndSortingRepository, CrudRepository { } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/service/IFooService.java ================================================ package org.springdoc.demo.resource.service; import java.util.Optional; import org.springdoc.demo.resource.model.Foo; public interface IFooService { Optional findById(Long id); Foo save(Foo foo); Iterable findAll(); } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/service/impl/FooServiceImpl.java ================================================ package org.springdoc.demo.resource.service.impl; import java.util.Optional; import org.springdoc.demo.resource.model.Foo; import org.springdoc.demo.resource.repository.IFooRepository; import org.springdoc.demo.resource.service.IFooService; import org.springframework.stereotype.Service; @Service public class FooServiceImpl implements IFooService { private IFooRepository fooRepository; public FooServiceImpl(IFooRepository fooRepository) { this.fooRepository = fooRepository; } @Override public Optional findById(Long id) { return fooRepository.findById(id); } @Override public Foo save(Foo foo) { return fooRepository.save(foo); } @Override public Iterable findAll() { return fooRepository.findAll(); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/web/controller/FooController.java ================================================ package org.springdoc.demo.resource.web.controller; import java.util.ArrayList; import java.util.Collection; import java.util.List; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springdoc.demo.resource.model.Foo; import org.springdoc.demo.resource.service.IFooService; import org.springdoc.demo.resource.web.dto.FooDTO; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; @RestController @RequestMapping(value = "/api/foos") @SecurityRequirement(name = "security_auth") public class FooController { private IFooService fooService; public FooController(IFooService fooService) { this.fooService = fooService; } @GetMapping(value = "/{id}") public FooDTO findOne(@PathVariable Long id) { Foo entity = fooService.findById(id) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); return convertToDto(entity); } @ResponseStatus(HttpStatus.CREATED) @PostMapping public void create(@RequestBody FooDTO newFoo) { Foo entity = convertToEntity(newFoo); this.fooService.save(entity); } @GetMapping public Collection findAll() { Iterable foos = this.fooService.findAll(); List fooDtos = new ArrayList<>(); foos.forEach(p -> fooDtos.add(convertToDto(p))); return fooDtos; } @PutMapping("/{id}") public FooDTO updateFoo(@PathVariable("id") Long id, @RequestBody FooDTO updatedFoo) { Foo fooEntity = convertToEntity(updatedFoo); return this.convertToDto(this.fooService.save(fooEntity)); } protected FooDTO convertToDto(Foo entity) { FooDTO dto = new FooDTO(entity.getId(), entity.getName()); return dto; } protected Foo convertToEntity(FooDTO dto) { Foo foo = new Foo(dto.getName()); if (!StringUtils.isEmpty(dto.getId())) { foo.setId(dto.getId()); } return foo; } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/web/controller/UserInfoController.java ================================================ package org.springdoc.demo.resource.web.controller; import java.util.Collections; import java.util.Map; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserInfoController { @GetMapping("/user/info") @SecurityRequirement(name = "security_auth") public Map getUserInfo(@AuthenticationPrincipal Jwt principal) { return Collections.singletonMap("user_name", principal.getClaimAsString("sub")); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/java/org/springdoc/demo/resource/web/dto/FooDTO.java ================================================ package org.springdoc.demo.resource.web.dto; public class FooDTO { private long id; private String name; public FooDTO() { super(); } public FooDTO(final long id, final String name) { super(); this.id = id; this.name = name; } // public long getId() { return id; } public void setId(final long id) { this.id = id; } public String getName() { return name; } public void setName(final String name) { this.name = name; } } ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/resources/application.yml ================================================ server: forward-headers-strategy: framework port: 8082 ####### resource server configuration properties spring: webflux: base-path: /resource-server jpa: defer-datasource-initialization: true security: oauth2: resourceserver: jwt: issuer-uri: ${OAUTH2_SERVER:http://127.0.0.1:8083} springdoc: version: '@springdoc.version@' swagger-ui: oauth: clientId: newClient clientSecret: newClientSecret oAuthFlow: authorizationUrl: ${OAUTH2_SERVER:http://127.0.0.1:8083}/oauth2/authorize tokenUrl: ${OAUTH2_SERVER:http://127.0.0.1:8083}/oauth2/token ================================================ FILE: demo-oauth2/oauth-resource-server-webflux/src/main/resources/data.sql ================================================ INSERT INTO Foo(id, name) VALUES (1, 'Foo 1'); INSERT INTO Foo(id, name) VALUES (2, 'Foo 2'); INSERT INTO Foo(id, name) VALUES (3, 'Foo 3'); ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/pom.xml ================================================ 4.0.0 org.springdoc demo-oauth2 3.1.7-SNAPSHOT oauth-resource-server-webmvc org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-oauth2-resource-server org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 org.springdoc springdoc-openapi-starter-webmvc-ui ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/ResourceServerApp.java ================================================ package org.springdoc.demo.resource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceServerApp { public static void main(String[] args) throws Exception { SpringApplication.run(ResourceServerApp.class, args); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/config/OpenApiConfig.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.resource.config; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.security.OAuthFlow; import io.swagger.v3.oas.annotations.security.OAuthFlows; import io.swagger.v3.oas.annotations.security.OAuthScope; import io.swagger.v3.oas.annotations.security.SecurityScheme; @OpenAPIDefinition(info = @Info(title = "Foo API", description = "Foo description", version = "v1")) @SecurityScheme(name = "security_auth", type = SecuritySchemeType.OAUTH2, flows = @OAuthFlows(authorizationCode = @OAuthFlow( authorizationUrl = "${springdoc.oAuthFlow.authorizationUrl}" , tokenUrl = "${springdoc.oAuthFlow.tokenUrl}", scopes = { @OAuthScope(name = "springdoc.read", description = "read scope"), @OAuthScope(name = "springdoc.write", description = "write scope") }))) public class OpenApiConfig {} ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/config/SecurityConfig.java ================================================ package org.springdoc.demo.resource.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz -> authz .requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() .requestMatchers(HttpMethod.GET, "/user/info", "/api/foos/**") .hasAuthority("SCOPE_springdoc.read") .requestMatchers(HttpMethod.POST, "/api/foos") .hasAuthority("SCOPE_springdoc.write") .anyRequest() .authenticated()) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/model/Foo.java ================================================ package org.springdoc.demo.resource.model; import java.util.UUID; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Foo { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; private String name; protected Foo() { } public Foo(String name) { this.name = name; } public UUID getId() { return id; } public void setId(UUID id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Foo other = (Foo) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "Foo [id=" + id + ", name=" + name + "]"; } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/repository/IFooRepository.java ================================================ package org.springdoc.demo.resource.repository; import org.springdoc.demo.resource.model.Foo; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; public interface IFooRepository extends PagingAndSortingRepository, CrudRepository { } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/service/IFooService.java ================================================ package org.springdoc.demo.resource.service; import java.util.Optional; import org.springdoc.demo.resource.model.Foo; public interface IFooService { Optional findById(Long id); Foo save(Foo foo); Iterable findAll(); } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/service/impl/FooServiceImpl.java ================================================ package org.springdoc.demo.resource.service.impl; import java.util.Optional; import org.springdoc.demo.resource.model.Foo; import org.springdoc.demo.resource.repository.IFooRepository; import org.springdoc.demo.resource.service.IFooService; import org.springframework.stereotype.Service; @Service public class FooServiceImpl implements IFooService { private IFooRepository fooRepository; public FooServiceImpl(IFooRepository fooRepository) { this.fooRepository = fooRepository; } @Override public Optional findById(Long id) { return fooRepository.findById(id); } @Override public Foo save(Foo foo) { return fooRepository.save(foo); } @Override public Iterable findAll() { return fooRepository.findAll(); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/web/controller/FooController.java ================================================ package org.springdoc.demo.resource.web.controller; import java.util.ArrayList; import java.util.Collection; import java.util.List; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springdoc.demo.resource.model.Foo; import org.springdoc.demo.resource.service.IFooService; import org.springdoc.demo.resource.web.dto.FooDTO; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; @RestController @RequestMapping(value = "/api/foos") @SecurityRequirement(name = "security_auth") public class FooController { private IFooService fooService; public FooController(IFooService fooService) { this.fooService = fooService; } @GetMapping(value = "/{id}") public FooDTO findOne(@PathVariable Long id) { Foo entity = fooService.findById(id) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); return convertToDto(entity); } @ResponseStatus(HttpStatus.CREATED) @PostMapping public void create(@RequestBody FooDTO newFoo) { Foo entity = convertToEntity(newFoo); this.fooService.save(entity); } @GetMapping public Collection findAll() { Iterable foos = this.fooService.findAll(); List fooDtos = new ArrayList<>(); foos.forEach(p -> fooDtos.add(convertToDto(p))); return fooDtos; } @PutMapping("/{id}") public FooDTO updateFoo(@PathVariable("id") Long id, @RequestBody FooDTO updatedFoo) { Foo fooEntity = convertToEntity(updatedFoo); return this.convertToDto(this.fooService.save(fooEntity)); } protected FooDTO convertToDto(Foo entity) { FooDTO dto = new FooDTO(entity.getId(), entity.getName()); return dto; } protected Foo convertToEntity(FooDTO dto) { Foo foo = new Foo(dto.getName()); if (!StringUtils.isEmpty(dto.getId())) { foo.setId(dto.getId()); } return foo; } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/web/controller/UserInfoController.java ================================================ package org.springdoc.demo.resource.web.controller; import java.util.Collections; import java.util.Map; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserInfoController { @GetMapping("/user/info") @SecurityRequirement(name = "security_auth") public Map getUserInfo(@AuthenticationPrincipal Jwt principal) { return Collections.singletonMap("user_name", principal.getClaimAsString("sub")); } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/java/org/springdoc/demo/resource/web/dto/FooDTO.java ================================================ package org.springdoc.demo.resource.web.dto; import java.util.UUID; public class FooDTO { private UUID id; private String name; public FooDTO() { super(); } public FooDTO(final UUID id, final String name) { super(); this.id = id; this.name = name; } public UUID getId() { return id; } public void setId(UUID id) { this.id = id; } public String getName() { return name; } public void setName(final String name) { this.name = name; } } ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/resources/application.yml ================================================ server: port: 8081 forward-headers-strategy: framework servlet: context-path: /resource-server ####### resource server configuration properties spring: jpa: defer-datasource-initialization: true security: oauth2: resourceserver: jwt: issuer-uri: ${OAUTH2_SERVER:http://127.0.0.1:8083} springdoc: version: '@springdoc.version@' swagger-ui: oauth: clientId: newClient clientSecret: newClientSecret oAuthFlow: authorizationUrl: ${OAUTH2_SERVER:http://127.0.0.1:8083}/oauth2/authorize tokenUrl: ${OAUTH2_SERVER:http://127.0.0.1:8083}/oauth2/token ================================================ FILE: demo-oauth2/oauth-resource-server-webmvc/src/main/resources/data.sql ================================================ INSERT INTO Foo(id, name) VALUES ('dc58c780-a571-437e-85e2-9be232c5cd80', 'Foo 1'); INSERT INTO Foo(id, name) VALUES ('dc58c780-a571-437e-85e2-9be232c5cd84', 'Foo 2'); INSERT INTO Foo(id, name) VALUES ('dc58c780-a571-437e-85e2-9be232c5cd83', 'Foo 3'); ================================================ FILE: demo-oauth2/pom.xml ================================================ 4.0.0 springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT pom demo-oauth2 oauth-authorization-server oauth-resource-server-webflux oauth-resource-server-webmvc org.apache.maven.plugins maven-surefire-plugin **/*LiveTest.java ================================================ FILE: demo-person-service/.gitattributes ================================================ # # https://help.github.com/articles/dealing-with-line-endings/ # # These are explicitly windows files and should use crlf *.bat text eol=crlf ================================================ FILE: demo-person-service/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-person-service/README.md ================================================ ### Relevant Articles: - [DZone Part1](https://dzone.com/articles/openapi-3-documentation-with-spring-boot) - [DZone Part2](https://dzone.com/articles/doing-more-with-springdoc-openapi) ### Building native image with GraalVM To create a `native image`, Run the following command ```sh mvn -Pnative clean native:compile ``` ================================================ FILE: demo-person-service/pom.xml ================================================ 4.0.0 springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT Demo project for Spring Boot with openapi 3 documentation demo-person-service org.springframework.boot spring-boot-starter-web org.springdoc springdoc-openapi-starter-webmvc-ui org.javamoney.moneta moneta-core 1.3 org.zalando jackson-datatype-money 1.1.1 org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/SampleApplication.java ================================================ package org.springdoc.demo.services.person; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication() public class SampleApplication { public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components().addSecuritySchemes("basicScheme", new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic"))) .info(new Info().title("Person API").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } } ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/config/SampleConfig.java ================================================ package org.springdoc.demo.services.person.config; import javax.money.MonetaryAmount; import jakarta.annotation.PostConstruct; import org.javamoney.moneta.Money; import org.zalando.jackson.datatype.money.MoneyModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.springdoc.core.utils.SpringDocUtils.getConfig; @Configuration public class SampleConfig { @PostConstruct public void initConfig() { getConfig().replaceWithClass(MonetaryAmount.class, org.springdoc.core.converters.models.MonetaryAmount.class); } @Bean public MoneyModule moneyModule() { return new MoneyModule().withMonetaryAmount(Money::of); } } ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/controller/PersonController.java ================================================ package org.springdoc.demo.services.person.controller; import java.util.ArrayList; import java.util.List; import java.util.Random; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import org.springdoc.demo.services.person.model.Person; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class PersonController { private Random ran = new Random(); @PostMapping("/persons") public Person person(@Valid @RequestBody Person person) { int nxt = ran.nextInt(10); if (nxt >= 5) { throw new RuntimeException("Breaking logic"); } return person; } @GetMapping(path = "/persons") public List findByLastName(@RequestParam @NotBlank @Size(max = 10) String lastName) { List hardCoded = new ArrayList<>(); Person person = new Person(); person.setAge(20); person.setEmail1("abc1@abc.com"); person.setEmail2("abc@abc.com"); person.setFirstName("Somefirstname"); person.setLastName(lastName); person.setId(1); hardCoded.add(person); return hardCoded; } } ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/exceptions/ErrorMessage.java ================================================ package org.springdoc.demo.services.person.exceptions; import java.util.Arrays; import java.util.Collections; import java.util.List; public class ErrorMessage { private List errors; public ErrorMessage() { } public ErrorMessage(List errors) { this.errors = errors; } public ErrorMessage(String error) { this(Collections.singletonList(error)); } public ErrorMessage(String... errors) { this(Arrays.asList(errors)); } public List getErrors() { return errors; } public void setErrors(List errors) { this.errors = errors; } } ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/exceptions/GlobalControllerAdvice.java ================================================ package org.springdoc.demo.services.person.exceptions; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.demo.services.person.controller.PersonController; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; @ControllerAdvice(assignableTypes = PersonController.class) public class GlobalControllerAdvice { /** * Note use base class if you wish to leverage its handling. * Some code will need changing. */ private static final Logger logger = LoggerFactory.getLogger(GlobalControllerAdvice.class); @ExceptionHandler(Throwable.class) @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR) public ResponseEntity problem(final Throwable e) { String message = e.getMessage(); //might actually prefer to use a geeric mesasge message = "Problem occured"; UUID uuid = UUID.randomUUID(); String logRef = uuid.toString(); logger.error("logRef=" + logRef, message, e); return new ResponseEntity(new Problem(logRef, message), HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(code = HttpStatus.BAD_REQUEST) public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex ) { List fieldErrors = ex.getBindingResult().getFieldErrors(); List globalErrors = ex.getBindingResult().getGlobalErrors(); List errors = new ArrayList<>(fieldErrors.size() + globalErrors.size()); String error; for (FieldError fieldError : fieldErrors) { error = fieldError.getField() + ", " + fieldError.getDefaultMessage(); errors.add(error); } for (ObjectError objectError : globalErrors) { error = objectError.getObjectName() + ", " + objectError.getDefaultMessage(); errors.add(error); } ErrorMessage errorMessage = new ErrorMessage(errors); //Object result=ex.getBindingResult();//instead of above can allso pass the more detailed bindingResult return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST); } @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(code = HttpStatus.BAD_REQUEST) public ResponseEntity handleConstraintViolatedException(ConstraintViolationException ex ) { Set> constraintViolations = ex.getConstraintViolations(); List errors = new ArrayList<>(constraintViolations.size()); String error; for (ConstraintViolation constraintViolation : constraintViolations) { error = constraintViolation.getMessage(); errors.add(error); } ErrorMessage errorMessage = new ErrorMessage(errors); return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST); } @ExceptionHandler(MissingServletRequestParameterException.class) @ResponseStatus(code = HttpStatus.BAD_REQUEST) public ResponseEntity handleMissingServletRequestParameterException(MissingServletRequestParameterException ex ) { List errors = new ArrayList<>(); String error = ex.getParameterName() + ", " + ex.getMessage(); errors.add(error); ErrorMessage errorMessage = new ErrorMessage(errors); return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST); } @ExceptionHandler(HttpMediaTypeNotSupportedException.class) @ResponseStatus(code = HttpStatus.UNSUPPORTED_MEDIA_TYPE) public ResponseEntity handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex ) { String unsupported = "Unsupported content type: " + ex.getContentType(); String supported = "Supported content types: " + MediaType.toString(ex.getSupportedMediaTypes()); ErrorMessage errorMessage = new ErrorMessage(unsupported, supported); return new ResponseEntity(errorMessage, HttpStatus.UNSUPPORTED_MEDIA_TYPE); } @ExceptionHandler(HttpMessageNotReadableException.class) @ResponseStatus(code = HttpStatus.BAD_REQUEST) public ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex) { ErrorMessage errorMessage = new ErrorMessage(ex.getMessage()); return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST); } } ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/exceptions/Problem.java ================================================ package org.springdoc.demo.services.person.exceptions; public class Problem { private String logRef; private String message; public Problem(String logRef, String message) { super(); this.logRef = logRef; this.message = message; } public Problem() { super(); } public String getLogRef() { return logRef; } public void setLogRef(String logRef) { this.logRef = logRef; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: demo-person-service/src/main/java/org/springdoc/demo/services/person/model/Person.java ================================================ package org.springdoc.demo.services.person.model; import javax.money.MonetaryAmount; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; public class Person { private long id; @Size(min = 2, max = 50) @NotBlank private String firstName; @Size(min = 2, max = 50) @NotBlank private String lastName; @Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address") private String email1; @Email private String email2; @Min(18) @Max(30) private int age; @JsonProperty private MonetaryAmount worth; public MonetaryAmount getWorth() { return worth; } public void setWorth(MonetaryAmount worth) { this.worth = worth; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getEmail1() { return email1; } public void setEmail1(String email1) { this.email1 = email1; } public String getEmail2() { return email2; } public void setEmail2(String email2) { this.email2 = email2; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ================================================ FILE: demo-person-service/src/main/resources/application.properties ================================================ springdoc.version=@springdoc.version@ springdoc.swagger-ui.use-root-path=true server.forward-headers-strategy=framework ================================================ FILE: demo-spring-boot-3-webflux/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-spring-boot-3-webflux/.java-version ================================================ graalvm64-17.0.5 ================================================ FILE: demo-spring-boot-3-webflux/README.md ================================================ # springdoc-openapi demo with spring-boot-2 web-flux ## Building application ### Pre-requisites - JDK 8+ - maven 3 - docker CLI ### Option 1: Building Executable JAR To create an `executable jar`, simply run: ```sh mvn clean package ``` ### Option 2: Building a non-native OCI Images To create a non-native OCI docker image, simply run: ```sh mvn clean spring-boot:build-image ``` ### Option 3: Building native image with GraalVM To create a `native image`, Run the following command ```sh mvn -Pnative clean native:compile ``` ## Running the native application To run the demo using docker, invoke the following: ```sh docker run --rm -p 8080:8082 springdoc-openapi-spring-boot-2-webflux:3.1.6-SNAPSHOT ``` ================================================ FILE: demo-spring-boot-3-webflux/pom.xml ================================================ 4.0.0 org.springdoc springdoc-openapi-demos 3.1.7-SNAPSHOT demo-spring-boot-3-webflux org.springdoc springdoc-openapi-starter-webflux-ui org.springframework.boot spring-boot-starter-webflux io.projectreactor reactor-test test org.springframework.boot spring-boot-starter-data-mongodb-reactive org.springframework.boot spring-boot-configuration-processor true org.springdoc springdoc-openapi-maven-plugin http://localhost:8080/v3/api-docs.yaml openapi.yaml integration-test generate org.springframework.boot spring-boot-maven-plugin -Dspring.application.admin.enabled=true start stop ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/WebfluxDemoApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; @SpringBootApplication @EnableReactiveMongoRepositories public class WebfluxDemoApplication { public static void main(String[] args) { SpringApplication.run(WebfluxDemoApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components().addSecuritySchemes("basicScheme", new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic"))) .info(new Info().title("Tweet API").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } @Bean public GroupedOpenApi storeOpenApi() { String[] paths = { "/tweets/**" }; return GroupedOpenApi.builder().group("tweets").pathsToMatch(paths) .build(); } @Bean public GroupedOpenApi userOpenApi() { String[] paths = { "/stream/**" }; String[] packagedToMatch = { "org.springdoc.demo.app3" }; return GroupedOpenApi.builder().group("x-stream").pathsToMatch(paths).packagesToScan(packagedToMatch) .build(); } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/controller/ExceptionTranslator.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.controller; import org.springdoc.demo.app3.exception.TweetNotFoundException; import org.springdoc.demo.app3.payload.ErrorResponse; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class ExceptionTranslator { @SuppressWarnings("rawtypes") @ExceptionHandler(DuplicateKeyException.class) @ResponseStatus(HttpStatus.CONFLICT) public ResponseEntity handleDuplicateKeyException(DuplicateKeyException ex) { return ResponseEntity.status(HttpStatus.CONFLICT) .body(new ErrorResponse("A Tweet with the same text already exists")); } @SuppressWarnings("rawtypes") @ExceptionHandler(TweetNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ResponseEntity handleTweetNotFoundException(TweetNotFoundException ex) { return ResponseEntity.notFound().build(); } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/controller/TweetController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import org.springdoc.demo.app3.dto.TweetDTO; import org.springdoc.demo.app3.model.Tweet; import org.springdoc.demo.app3.repository.TweetRepository; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; /** * Created by bnasslahsen */ @RestController public class TweetController { private final TweetRepository tweetRepository; private final TweetMapper tweetMapper; public TweetController(TweetRepository tweetRepository, TweetMapper tweetMapper) { this.tweetRepository = tweetRepository; this.tweetMapper = tweetMapper; } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "get All Tweets") }) @GetMapping("/tweets") public Flux getAllTweets() { Flux tweet = tweetRepository.findAll(); return tweetMapper.toDTO(tweet); } @PostMapping("/tweets") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "create Tweets") }) public Mono createTweets(@Valid @RequestBody TweetDTO tweetDTO) { return tweetRepository.save(tweetMapper.toEntity(tweetDTO)).map(tweetMapper::toDTO); } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "get Tweet By Id"), @ApiResponse(responseCode = "404", description = "tweet not found") }) @GetMapping("/tweets/{id}") public Mono> getTweetById(@PathVariable(value = "id") String tweetId) { return tweetRepository.findById(tweetId).map(savedTweet -> ResponseEntity.ok(tweetMapper.toDTO(savedTweet))) .defaultIfEmpty(ResponseEntity.notFound().build()); } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "update Tweet"), @ApiResponse(responseCode = "404", description = "tweet not found") }) @PutMapping("/tweets/{id}") public Mono> updateTweet(@PathVariable(value = "id") String tweetId, @Valid @RequestBody TweetDTO tweetDTO) { return tweetRepository.findById(tweetId).flatMap(existingTweet -> { existingTweet.setText(tweetMapper.toEntity(tweetDTO).getText()); return tweetRepository.save(existingTweet); }).map(updateTweet -> new ResponseEntity<>(tweetMapper.toDTO(updateTweet), HttpStatus.OK)) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "delete Tweet"), @ApiResponse(responseCode = "404", description = "tweet not found") }) @DeleteMapping("/tweets/{id}") public Mono> deleteTweet(@PathVariable(value = "id") String tweetId) { return tweetRepository.findById(tweetId) .flatMap(existingTweet -> tweetRepository.delete(existingTweet) .then(Mono.just(new ResponseEntity(HttpStatus.OK)))) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @Operation(description = "Tweets are Sent to the client as Server Sent Events", responses = { @ApiResponse(responseCode = "200", description = "stream All Tweets") }) @GetMapping(value = "/stream/tweets", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux streamAllTweets() { return tweetMapper.toDTO(tweetRepository.findAll()); } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/controller/TweetMapper.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.controller; import org.springdoc.demo.app3.dto.TweetDTO; import org.springdoc.demo.app3.model.Tweet; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; @Component public class TweetMapper { Flux toDTO(Flux tweet) { return tweet.map(this::toDTO); } Flux toEntity(Flux tweetDTO) { return tweetDTO.map(this::toEntity); } Mono toDTO(Mono tweet) { return tweet.map(this::toDTO); } Mono toEntity(Mono tweetDTO) { return tweetDTO.map(this::toEntity); } TweetDTO toDTO(Tweet tweet) { TweetDTO teTweetDTO = new TweetDTO(); BeanUtils.copyProperties(tweet,teTweetDTO); return teTweetDTO; } Tweet toEntity(TweetDTO tweetDTO) { Tweet teTweet = new Tweet(); BeanUtils.copyProperties(tweetDTO, teTweet); return teTweet; } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/dto/TweetDTO.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.dto; import java.util.Date; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; /** * Created by bnasslahsen on 08/09/17. */ public class TweetDTO { private String id; @NotBlank @Size(max = 140) private String text; @NotNull private Date createdAt = new Date(); public TweetDTO() { } public TweetDTO(String text) { this.text = text; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/exception/TweetNotFoundException.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.exception; /** * Created by bnasslahsen */ public class TweetNotFoundException extends RuntimeException { /** * */ private static final long serialVersionUID = 1L; public TweetNotFoundException(String tweetId) { super("Tweet not found with id " + tweetId); } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/model/Tweet.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.model; import java.util.Date; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; /** * Created by bnasslahsen on 08/09/17. */ @Document(collection = "tweets") public class Tweet { @Id private String id; @NotBlank @Size(max = 140) private String text; @NotNull private Date createdAt = new Date(); public Tweet() { } public Tweet(String text) { this.text = text; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/payload/ErrorResponse.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.payload; /** * Created by bnasslahsen */ public class ErrorResponse { private String message; public ErrorResponse(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: demo-spring-boot-3-webflux/src/main/java/org/springdoc/demo/app3/repository/TweetRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.repository; import org.springdoc.demo.app3.model.Tweet; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.stereotype.Repository; /** * Created by bnasslahsen on 08/09/17. */ @Repository public interface TweetRepository extends ReactiveMongoRepository { } ================================================ FILE: demo-spring-boot-3-webflux/src/main/resources/META-INF/native-image/reflect-config.json ================================================ [ { "name": "org.springdoc.demo.app3.dto.TweetDTO[]" } ] ================================================ FILE: demo-spring-boot-3-webflux/src/main/resources/application.yml ================================================ server: port: 8082 forward-headers-strategy: framework spring: data: mongodb: uri: ${SPRING_DATA_MONGODB_URI:mongodb://localhost:27017/tweetsdb} springdoc: version: '@springdoc.version@' swagger-ui: use-root-path: true ================================================ FILE: demo-spring-boot-3-webflux-functional/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-spring-boot-3-webflux-functional/pom.xml ================================================ 4.0.0 springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT demo-spring-boot-3-webflux-functional org.springdoc springdoc-openapi-starter-webflux-ui org.springframework.boot spring-boot-starter-webflux io.projectreactor reactor-test test org.springframework.boot spring-boot-starter-data-r2dbc org.projectlombok lombok io.r2dbc r2dbc-h2 runtime org.springframework.boot spring-boot-configuration-processor true org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/AppNativeConfiguration.java ================================================ package org.springdoc.demo.app4; import java.util.Arrays; import org.springdoc.demo.app4.AppNativeConfiguration.AppNativeRuntimeHints; import org.springdoc.demo.app4.coffee.CoffeeService; import org.springdoc.demo.app4.employee.EmployeeRepository; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportRuntimeHints; /** * @author bnasslahsen */ @Configuration @ImportRuntimeHints(AppNativeRuntimeHints.class) public class AppNativeConfiguration { static Class[] applicationClasses = { org.springdoc.demo.app4.user.User[].class, org.springdoc.demo.app4.employee.Employee[].class, org.springdoc.demo.app4.coffee.Coffee[].class, org.springdoc.demo.app4.coffee.CoffeeOrder[].class, EmployeeRepository.class, CoffeeService.class }; static class AppNativeRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { Arrays.stream(applicationClasses).forEach(applicationClass -> hints.reflection().registerType(applicationClass, (hint) -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS))); } } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/WebfluxFunctionalDemoApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app4; import io.swagger.v3.oas.models.info.Info; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class WebfluxFunctionalDemoApplication { public static void main(String[] args) { SpringApplication.run(WebfluxFunctionalDemoApplication.class, args); } @Bean public GroupedOpenApi employeesOpenApi(@Value("${springdoc.version}") String appVersion) { String[] paths = { "/employees/**" }; return GroupedOpenApi.builder().group("employees") .addOpenApiCustomizer(openApi -> openApi.info(new Info().title("Employees API").version(appVersion))) .pathsToMatch(paths) .build(); } @Bean public GroupedOpenApi userOpenApi(@Value("${springdoc.version}") String appVersion) { String[] paths = { "/api/user/**" }; return GroupedOpenApi.builder().group("users") .addOpenApiCustomizer(openApi -> openApi.info(new Info().title("Users API").version(appVersion))) .pathsToMatch(paths) .build(); } @Bean public GroupedOpenApi coffeeOpenApi(@Value("${springdoc.version}") String appVersion) { String[] paths = { "/coffees/**" }; return GroupedOpenApi.builder().group("coffees") .addOpenApiCustomizer(openApi -> openApi.info(new Info().title("Coffees API").version(appVersion))) .pathsToMatch(paths) .build(); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/coffee/Coffee.java ================================================ package org.springdoc.demo.app4.coffee; import java.util.Objects; import org.springframework.data.annotation.Id; public class Coffee { @Id private String id; private String name; public Coffee() { } public Coffee(String name) { this.name = name; } public Coffee(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Coffee coffee = (Coffee) o; return Objects.equals(id, coffee.id) && Objects.equals(name, coffee.name); } @Override public int hashCode() { return Objects.hash(id, name); } @Override public String toString() { return "Coffee{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/coffee/CoffeeOrder.java ================================================ package org.springdoc.demo.app4.coffee; import java.time.Instant; import java.util.Objects; public class CoffeeOrder { private String coffeeId; private Instant whenOrdered; public CoffeeOrder() { } public CoffeeOrder(String coffeeId, Instant whenOrdered) { this.coffeeId = coffeeId; this.whenOrdered = whenOrdered; } public String getCoffeeId() { return coffeeId; } public Instant getWhenOrdered() { return whenOrdered; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CoffeeOrder that = (CoffeeOrder) o; return Objects.equals(coffeeId, that.coffeeId) && Objects.equals(whenOrdered, that.whenOrdered); } @Override public int hashCode() { return Objects.hash(coffeeId, whenOrdered); } @Override public String toString() { return "CoffeeOrder{" + "coffeeId='" + coffeeId + '\'' + ", whenOrdered=" + whenOrdered + '}'; } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/coffee/CoffeeRepository.java ================================================ package org.springdoc.demo.app4.coffee; import org.springframework.data.repository.reactive.ReactiveCrudRepository; public interface CoffeeRepository extends ReactiveCrudRepository { } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/coffee/CoffeeService.java ================================================ package org.springdoc.demo.app4.coffee; import java.time.Duration; import java.time.Instant; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.stereotype.Service; @Service public class CoffeeService { private final CoffeeRepository repo; public CoffeeService(CoffeeRepository repo) { this.repo = repo; } Flux getAllCoffees() { return repo.findAll(); } Mono getCoffeeById(@Parameter(in = ParameterIn.PATH) String id) { return repo.findById(id); } Flux getOrdersForCoffeeById(@Parameter(in = ParameterIn.PATH) String coffeeId) { return Flux.interval(Duration.ofSeconds(1)) .onBackpressureDrop() .map(i -> new CoffeeOrder(coffeeId, Instant.now())); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/coffee/RouteConfig.java ================================================ package org.springdoc.demo.app4.coffee; import reactor.core.publisher.Mono; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route; @Configuration public class RouteConfig { private final CoffeeService service; public RouteConfig(CoffeeService service) { this.service = service; } @Bean RouterFunction routerFunction() { return route().GET("/coffees", this::all, ops -> ops.beanClass(CoffeeService.class).beanMethod("getAllCoffees")).build() .and(route().GET("/coffees/{id}", this::byId, ops -> ops.beanClass(CoffeeService.class).beanMethod("getCoffeeById")).build()) .and(route().GET("/coffees/{id}/orders", this::orders, ops -> ops.beanClass(CoffeeService.class).beanMethod("getOrdersForCoffeeById")).build()); } private Mono all(ServerRequest req) { return ServerResponse.ok() .body(service.getAllCoffees(), Coffee.class); } private Mono byId(ServerRequest req) { return ServerResponse.ok() .body(service.getCoffeeById(req.pathVariable("id")), Coffee.class); } private Mono orders(ServerRequest req) { return ServerResponse.ok() .contentType(MediaType.TEXT_EVENT_STREAM) .body(service.getOrdersForCoffeeById(req.pathVariable("id")), CoffeeOrder.class); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/employee/Employee.java ================================================ package org.springdoc.demo.app4.employee; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class Employee { private String id; private String name; // standard getters and setters } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/employee/EmployeeFunctionalConfig.java ================================================ package org.springdoc.demo.app4.employee; import java.util.function.Consumer; import io.swagger.v3.oas.annotations.enums.ParameterIn; import org.springdoc.core.fn.builders.operation.Builder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.BodyExtractors; import org.springframework.web.reactive.function.server.HandlerFunction; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; import static org.springframework.web.reactive.function.server.ServerResponse.ok; @Configuration public class EmployeeFunctionalConfig { @Bean EmployeeRepository employeeRepository() { return new EmployeeRepository(); } @Bean RouterFunction getAllEmployeesRoute() { return route() .GET("/employees", accept(MediaType.APPLICATION_JSON), findAllEmployeesFunction(), getOpenAPI("findAllEmployees")).build(); } @Bean RouterFunction getEmployeeByIdRoute() { return route().GET("/employees/{id}", findEmployeeByIdFunction(), findEmployeeByIdOpenAPI()).build(); } @Bean RouterFunction updateEmployeeRoute() { return route().POST("/employees/update", accept(MediaType.APPLICATION_XML), updateEmployeeFunction(), getOpenAPI("updateEmployee")).build(); } RouterFunction composedRoutes() { return route().GET("/employees-composed", findAllEmployeesFunction(), getOpenAPI("findAllEmployees")).build() .and(route().GET("/employees-composed/{id}", findEmployeeByIdFunction(), findEmployeeByIdOpenAPI()).build()) .and(route().POST("/employees-composed/update", updateEmployeeFunction(), getOpenAPI("updateEmployee")).build()); } private HandlerFunction findAllEmployeesFunction() { return req -> ok().body( employeeRepository().findAllEmployees(), Employee.class); } private HandlerFunction updateEmployeeFunction() { return req -> req.body(BodyExtractors.toMono(Employee.class)) .doOnNext(employeeRepository()::updateEmployee) .then(ok().build()); } private HandlerFunction findEmployeeByIdFunction() { return req -> ok().body( employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class); } private Consumer getOpenAPI(String findAllEmployees) { return ops -> ops.beanClass(EmployeeRepository.class).beanMethod(findAllEmployees); } private Consumer findEmployeeByIdOpenAPI() { return ops -> ops.tag("employee") .operationId("findEmployeeById").summary("Find purchase order by ID").tags(new String[] { "MyEmployee" }) .parameter(parameterBuilder().in(ParameterIn.PATH).name("id").description("Employee Id")) .response(responseBuilder().responseCode("200").description("successful operation").implementation(Employee.class)) .response(responseBuilder().responseCode("400").description("Invalid Employee ID supplied")) .response(responseBuilder().responseCode("404").description("Employee not found")); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/employee/EmployeeRepository.java ================================================ package org.springdoc.demo.app4.employee; import java.util.HashMap; import java.util.Map; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Tag(name = "Employees") public class EmployeeRepository { static Map employeeData; static Map employeeAccessData; static { employeeData = new HashMap<>(); employeeData.put("1", new Employee("1", "Employee 1")); employeeData.put("2", new Employee("2", "Employee 2")); employeeData.put("3", new Employee("3", "Employee 3")); employeeData.put("4", new Employee("4", "Employee 4")); employeeData.put("5", new Employee("5", "Employee 5")); employeeData.put("6", new Employee("6", "Employee 6")); employeeData.put("7", new Employee("7", "Employee 7")); employeeData.put("8", new Employee("8", "Employee 8")); employeeData.put("9", new Employee("9", "Employee 9")); employeeData.put("10", new Employee("10", "Employee 10")); employeeAccessData = new HashMap<>(); employeeAccessData.put("1", "Employee 1 Access Key"); employeeAccessData.put("2", "Employee 2 Access Key"); employeeAccessData.put("3", "Employee 3 Access Key"); employeeAccessData.put("4", "Employee 4 Access Key"); employeeAccessData.put("5", "Employee 5 Access Key"); employeeAccessData.put("6", "Employee 6 Access Key"); employeeAccessData.put("7", "Employee 7 Access Key"); employeeAccessData.put("8", "Employee 8 Access Key"); employeeAccessData.put("9", "Employee 9 Access Key"); employeeAccessData.put("10", "Employee 10 Access Key"); } public Mono findEmployeeById(@Parameter(in = ParameterIn.PATH) String id) { return Mono.just(employeeData.get(id)); } public Flux findAllEmployees() { return Flux.fromIterable(employeeData.values()); } public Mono updateEmployee(Employee employee) { Employee existingEmployee = employeeData.get(employee.getId()); if (existingEmployee != null) { existingEmployee.setName(employee.getName()); } return Mono.just(existingEmployee); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/user/RoutingConfiguration.java ================================================ package org.springdoc.demo.app4.user; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.reactive.function.server.RequestPredicates.accept; @Configuration public class RoutingConfiguration { @Bean public RouterFunction monoRouterFunction(UserHandler userHandler) { return route().GET("/api/user/index", accept(APPLICATION_JSON), userHandler::getAll, ops -> ops.beanClass(UserRepository.class).beanMethod("getAllUsers")).build() .and(route().GET("/api/user/{id}", accept(APPLICATION_JSON), userHandler::getUser, ops -> ops.beanClass(UserRepository.class).beanMethod("getUserById")).build() .and(route().POST("/api/user/post", accept(APPLICATION_JSON), userHandler::postUser, ops -> ops.beanClass(UserRepository.class).beanMethod("saveUser")).build()) .and(route().PUT("/api/user/put/{id}", accept(APPLICATION_JSON), userHandler::putUser, ops -> ops.beanClass(UserRepository.class).beanMethod("putUser")).build()) .and(route().DELETE("/api/user/delete/{id}", accept(APPLICATION_JSON), userHandler::deleteUser, ops -> ops.beanClass(UserRepository.class).beanMethod("deleteUser")).build())); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/user/User.java ================================================ package org.springdoc.demo.app4.user; public class User { private long id; private String firstname; private String lastname; private int age; public User() { } public User(long id, String firstname, String lastname, int age) { this.id = id; this.firstname = firstname; this.lastname = lastname; this.age = age; } public long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { String info = String.format("id = %d, firstname = %s, lastname = %s, age = %d", id, firstname, lastname, age); return info; } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/user/UserHandler.java ================================================ package org.springdoc.demo.app4.user; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import static org.springframework.web.reactive.function.BodyInserters.fromObject; @Component public class UserHandler { private final UserRepository customerRepository; public UserHandler(UserRepository repository) { this.customerRepository = repository; } /** * GET ALL Users */ public Mono getAll(ServerRequest request) { // fetch all customers from repository Flux customers = customerRepository.getAllUsers(); // build response return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(customers, User.class); } /** * GET a User by ID */ public Mono getUser(ServerRequest request) { // parse path-variable long customerId = Long.valueOf(request.pathVariable("id")); // build notFound response Mono notFound = ServerResponse.notFound().build(); // get customer from repository Mono customerMono = customerRepository.getUserById(customerId); // build response return customerMono .flatMap(customer -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(customer))) .switchIfEmpty(notFound); } /** * POST a User */ public Mono postUser(ServerRequest request) { Mono customer = request.bodyToMono(User.class); return ServerResponse.ok().build(customerRepository.saveUser(customer)); } /** * PUT a User */ public Mono putUser(ServerRequest request) { // parse id from path-variable long customerId = Long.valueOf(request.pathVariable("id")); // get customer data from request object Mono customer = request.bodyToMono(User.class); // get customer from repository Mono responseMono = customerRepository.putUser(customerId, customer); // build response return responseMono .flatMap(cust -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(cust))); } /** * DELETE a User */ public Mono deleteUser(ServerRequest request) { // parse id from path-variable long customerId = Long.valueOf(request.pathVariable("id")); // get customer from repository Mono responseMono = customerRepository.deleteUser(customerId); // build response return responseMono .flatMap(strMono -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(fromObject(strMono))); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/user/UserRepository.java ================================================ package org.springdoc.demo.app4.user; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Tag(name = "Users") public interface UserRepository { public Mono getUserById(@Parameter(in = ParameterIn.PATH, description = "The user Id") Long id); @Operation(description = "get all the users") public Flux getAllUsers(); @Operation(description = "get all the users by firstname") public Flux getAllUsers(String firstname); public Mono saveUser(Mono user); public Mono putUser(@Parameter(in = ParameterIn.PATH) Long id, Mono user); public Mono deleteUser(@Parameter(in = ParameterIn.PATH) Long id); } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/java/org/springdoc/demo/app4/user/UserRepositoryImpl.java ================================================ package org.springdoc.demo.app4.user; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import jakarta.annotation.PostConstruct; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.stereotype.Repository; @Repository public class UserRepositoryImpl implements UserRepository { private Map users = new HashMap(); @PostConstruct public void init() throws Exception { users.put(Long.valueOf(1), new User(1, "Jack", "Smith", 20)); users.put(Long.valueOf(2), new User(2, "Peter", "Johnson", 25)); } @Override public Mono getUserById(Long id) { return Mono.just(users.get(id)); } @Override public Flux getAllUsers() { return Flux.fromIterable(this.users.values()); } @Override public Flux getAllUsers(String firstname) { return Flux.fromIterable(this.users.values().stream().filter(user -> user.getFirstname().equals(firstname)).collect(Collectors.toList())); } @Override public Mono saveUser(Mono monoUser) { Mono userMono = monoUser.doOnNext(user -> { // do post users.put(user.getId(), user); // log on console System.out.println("########### POST:" + user); }); return userMono.then(); } @Override public Mono putUser(Long id, Mono monoUser) { Mono userMono = monoUser.doOnNext(user -> { // reset user.Id user.setId(id); // do put users.put(id, user); // log on console System.out.println("########### PUT:" + user); }); return userMono; } @Override public Mono deleteUser(Long id) { // delete processing users.remove(id); return Mono.just("Delete Succesfully!"); } } ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/resources/application.yml ================================================ server: servlet: context-path: / compression: enabled: true mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json min-response-size: 1024 http2: enabled: true port: 8084 forward-headers-strategy: framework springdoc: version: '@springdoc.version@' swagger-ui: use-root-path: true ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/resources/logback-spring.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n true ================================================ FILE: demo-spring-boot-3-webflux-functional/src/main/resources/schema.sql ================================================ CREATE TABLE COFFEE ( id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) ); ================================================ FILE: demo-spring-boot-3-webmvc/.gitignore ================================================ /target/ ================================================ FILE: demo-spring-boot-3-webmvc/.java-version ================================================ graalvm64-17.0.5 ================================================ FILE: demo-spring-boot-3-webmvc/README.md ================================================ # springdoc-openapi demo with spring-boot-2 web-mvc ## Building application ### Pre-requisites - JDK 8+ - maven 3 - docker CLI ### Option 1: Building Executable JAR To create an `executable jar`, simply run: ```sh mvn clean package ``` ### Option 2: Building a non-native OCI Images To create a non-native OCI docker image, simply run: ```sh mvn clean spring-boot:build-image ``` ### Option 3: Building native image with GraalVM To create a `native image`, Run the following command ```sh mvn -Pnative clean native:compile ``` ## Running the native application To run the demo using docker, invoke the following: ```sh docker run --rm -p 8080:8081 springdoc-openapi-spring-boot-2-webmvc:3.1.6-SNAPSHOT ``` ================================================ FILE: demo-spring-boot-3-webmvc/pom.xml ================================================ 4.0.0 org.springdoc springdoc-openapi-demos 3.1.7-SNAPSHOT demo-spring-boot-3-webmvc org.springframework.boot spring-boot-starter-web org.springframework.data spring-data-commons org.springframework.boot spring-boot-starter-actuator com.fasterxml.jackson.dataformat jackson-dataformat-xml org.springdoc springdoc-openapi-starter-webmvc-ui org.springframework.boot spring-boot-configuration-processor true org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/Application.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import org.springdoc.core.customizers.OpenApiCustomizer; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; import static org.springdoc.core.utils.Constants.ALL_PATTERN; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } @Bean @Profile("!prod") public GroupedOpenApi actuatorApi(OpenApiCustomizer actuatorOpenApiCustomizer, OperationCustomizer actuatorCustomizer, WebEndpointProperties endpointProperties, @Value("${springdoc.version}") String appVersion) { return GroupedOpenApi.builder() .group("Actuator") .pathsToMatch(endpointProperties.getBasePath() + ALL_PATTERN) .addOpenApiCustomizer(actuatorOpenApiCustomizer) .addOpenApiCustomizer(openApi -> openApi.info(new Info().title("Actuator API").version(appVersion))) .addOperationCustomizer(actuatorCustomizer) .pathsToExclude("/health/*") .build(); } @Bean public GroupedOpenApi usersGroup(@Value("${springdoc.version}") String appVersion) { return GroupedOpenApi.builder().group("users") .addOperationCustomizer((operation, handlerMethod) -> { operation.addSecurityItem(new SecurityRequirement().addList("basicScheme")); return operation; }) .addOpenApiCustomizer(openApi -> openApi.info(new Info().title("Users API").version(appVersion))) .packagesToScan("org.springdoc.demo.app2") .pathsToMatch("/user/**") .build(); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/ApiUtil.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.io.IOException; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.server.ResponseStatusException; public class ApiUtil { public static void setExampleResponse(NativeWebRequest req, String contentType, String example) { try { req.getNativeResponse(HttpServletResponse.class).addHeader("Content-Type", contentType); req.getNativeResponse(HttpServletResponse.class).getOutputStream().print(example); } catch (IOException e) { throw new RuntimeException(e); } } public static void checkApiKey(NativeWebRequest req) { if (!"1".equals(System.getenv("DISABLE_API_KEY")) && !"special-key".equals(req.getHeader("api_key"))) { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing API key!"); } } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/ExceptionTranslator.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.Map; import jakarta.validation.ConstraintViolationException; import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.WebRequest; @RestControllerAdvice public class ExceptionTranslator { private final ErrorAttributes errorAttributes; public ExceptionTranslator(ErrorAttributes errorAttributes) { this.errorAttributes = errorAttributes; } @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Map processConstraintViolationException(WebRequest request) { request.setAttribute("jakarta.servlet.error.status_code", HttpStatus.BAD_REQUEST.value(), RequestAttributes.SCOPE_REQUEST); return errorAttributes.getErrorAttributes(request, ErrorAttributeOptions.defaults()); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/PetApi.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ /** * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (3.0.0). * https://openapi-generator.tech * Do not edit the class manually. */ package org.springdoc.demo.app2.api; import java.util.List; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.enums.ParameterStyle; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.OAuthFlow; import io.swagger.v3.oas.annotations.security.OAuthFlows; import io.swagger.v3.oas.annotations.security.OAuthScope; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityScheme; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springdoc.core.annotations.ParameterObject; import org.springdoc.demo.app2.model.ModelApiResponse; import org.springdoc.demo.app2.model.Pet; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; @SecurityScheme(name = "petstore_auth", type = SecuritySchemeType.OAUTH2, flows = @OAuthFlows(implicit = @OAuthFlow(authorizationUrl = "https://petstore3.swagger.io/oauth/authorize", scopes = { @OAuthScope(name = "write:pets", description = "modify pets in your account"), @OAuthScope(name = "read:pets", description = "read your pets") }))) @Tag(name = "pet", description = "the pet API") public interface PetApi { default PetApiDelegate getDelegate() { return new PetApiDelegate() { }; } @Operation(summary = "Add a new pet to the store", description = "Add a new pet to the store", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successful operation", content = { @Content(mediaType = "application/xml", schema = @Schema(implementation = Pet.class)), @Content(mediaType = "application/json", schema = @Schema(implementation = Pet.class)) }), @ApiResponse(responseCode = "405", description = "Invalid input") }) @PostMapping(value = "/pet", consumes = { "application/json", "application/xml", "application/x-www-form-urlencoded" }) default void addPet( @Parameter(description = "Create a new pet in the store", required = true) @Valid @RequestBody Pet pet) { // return getDelegate().addPet(pet); } @Operation(summary = "Deletes a pet", description = "", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "400", description = "Invalid pet value") }) @DeleteMapping(value = "/pet/{petId}") default ResponseEntity deletePet( @Parameter(description = "Pet id to delete", required = true) @PathVariable("petId") Long petId, @Parameter(description = "") @RequestHeader(value = "api_key", required = false) String apiKey) { return getDelegate().deletePet(petId, apiKey); } @Operation(summary = "Finds Pets by status", description = "Multiple status values can be provided with comma separated strings", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Pet.class)))), @ApiResponse(responseCode = "400", description = "Invalid status value") }) @GetMapping(value = "/pet/findByStatus", produces = { "application/xml", "application/json" }) default ResponseEntity> findPetsByStatus(@Parameter(explode = Explode.TRUE, name = "status", in = ParameterIn.QUERY, description = "Status values that need to be considered for filter", style = ParameterStyle.FORM, schema = @Schema(type = "string", defaultValue = "available", allowableValues = { "available", "pending", "sold" })) @Valid @RequestParam(value = "status", required = false) List status) { return getDelegate().findPetsByStatus(status); } @Operation(summary = "Finds Pets by tags", description = "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Pet.class)))), @ApiResponse(responseCode = "400", description = "Invalid tag value", content = @Content) }) @GetMapping(value = "/pet/findByTags", produces = { "application/xml", "application/json" }) default ResponseEntity> findPetsByTags( @Parameter(description = "Tags to filter by", explode = Explode.TRUE, in = ParameterIn.QUERY, name = "tags", style = ParameterStyle.FORM) @Valid @RequestParam(value = "tags", required = false) List tags) { return getDelegate().findPetsByTags(tags); } @Operation(summary = "Find pet by ID", description = "Returns a single pet", security = { @SecurityRequirement(name = "api_key"), @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(implementation = Pet.class))), @ApiResponse(responseCode = "400", description = "Invalid ID supplied", content = @Content), @ApiResponse(responseCode = "404", description = "Pet not found", content = @Content) }) @GetMapping(value = "/pet/{petId}", produces = { "application/xml", "application/json" }) default ResponseEntity getPetById( @Parameter(description = "ID of pet to return", required = true) @PathVariable("petId") Long petId) { return getDelegate().getPetById(petId); } @Operation(summary = "Update an existing pet", description = "Update an existing pet by Id", operationId = "updatePet", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successful operation", content = { @Content(mediaType = "application/xml", schema = @Schema(implementation = Pet.class)), @Content(mediaType = "application/json", schema = @Schema(implementation = Pet.class)) } ), @ApiResponse(responseCode = "400", description = "Invalid ID supplied"), @ApiResponse(responseCode = "404", description = "Pet not found"), @ApiResponse(responseCode = "405", description = "Validation exception") }) @PutMapping(value = "/pet", consumes = { "application/json", "application/xml", "application/x-www-form-urlencoded" }) default ResponseEntity updatePet( @Parameter(description = "Update an existent pet in the store", required = true) @Valid @RequestBody Pet pet) { return getDelegate().updatePet(pet); } @Operation(summary = "Updates a pet in the store with form data", description = "", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "405", description = "Invalid input") }) @PostMapping(value = "/pet/{petId}", consumes = { "application/x-www-form-urlencoded" }) default ResponseEntity updatePetWithForm( @Parameter(description = "ID of pet that needs to be updated", required = true) @PathVariable("petId") Long petId, @Parameter(description = "Name of pet that needs to be updated") @RequestParam(value = "name", required = false) String name, @Parameter(description = "Status of pet that needs to be updated") @RequestParam(value = "status", required = false) String status) { return getDelegate().updatePetWithForm(petId, name, status); } @Operation(summary = "uploads an image", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(implementation = ModelApiResponse.class))) }) @PostMapping(value = "/pet/{petId}/uploadImage", produces = { "application/json" }, consumes = { "multipart/form-data" }) default ResponseEntity uploadFile( @Parameter(description = "ID of pet to update", required = true) @PathVariable("petId") Long petId, @Parameter(description = "Additional Metadata") @RequestParam(value = "additionalMetadata", required = false) String additionalMetadata, @Parameter(content = @Content(mediaType = "multipart/form-data", schema = @Schema(type = "string", format = "binary"))) @Valid @RequestPart("file") MultipartFile file) { return getDelegate().uploadFile(petId, additionalMetadata, file); } @Operation(summary = "Get all Pets paged", description = "Get all Pets paged", security = { @SecurityRequirement(name = "petstore_auth", scopes = { "write:pets", "read:pets" }) }, tags = { "pet" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Pet.class)))) }) @GetMapping(value = "/pet", produces = { "application/xml", "application/json" }) default ResponseEntity> getAllPets(@NotNull @ParameterObject Pageable pageable) { return getDelegate().getAllPets(pageable); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/PetApiController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.Optional; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("${openapi.openAPIPetstore.base-path:/}") public class PetApiController implements PetApi { private final PetApiDelegate delegate; public PetApiController(@org.springframework.beans.factory.annotation.Autowired(required = false) PetApiDelegate delegate) { this.delegate = Optional.ofNullable(delegate).orElse(new PetApiDelegate() { }); } @Override public PetApiDelegate getDelegate() { return delegate; } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/PetApiDelegate.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.List; import java.util.Optional; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springdoc.demo.app2.model.ModelApiResponse; import org.springdoc.demo.app2.model.Pet; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.multipart.MultipartFile; /** * A delegate to be called by the {@link PetApiController}}. * Implement this interface with a {@link org.springframework.stereotype.Service} annotated class. */ public interface PetApiDelegate { default Optional getRequest() { return Optional.empty(); } /** * @see PetApi#addPet */ default void addPet(Pet pet) { } /** * @see PetApi#deletePet */ default ResponseEntity deletePet(Long petId, String apiKey) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see PetApi#findPetsByStatus */ default ResponseEntity> findPetsByStatus(List status) { extract(); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } default void extract() { getRequest().ifPresent(request -> { for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { ApiUtil.setExampleResponse(request, "application/json", "{ \"photoUrls\" : [ \"photoUrls\", \"photoUrls\" ], \"name\" : \"doggie\", \"id\" : 0, \"category\" : { \"name\" : \"name\", \"id\" : 6 }, \"tags\" : [ { \"name\" : \"name\", \"id\" : 1 }, { \"name\" : \"name\", \"id\" : 1 } ], \"status\" : \"available\"}"); break; } if (mediaType.isCompatibleWith(MediaType.valueOf("application/xml"))) { ApiUtil.setExampleResponse(request, "application/xml", " 123456789 doggie aeiou aeiou"); break; } } }); } /** * @see PetApi#findPetsByTags */ default ResponseEntity> findPetsByTags(List tags) { extract(); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see PetApi#getPetById */ default ResponseEntity getPetById(Long petId) { extract(); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see PetApi#updatePet */ default ResponseEntity updatePet(Pet pet) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see PetApi#updatePetWithForm */ default ResponseEntity updatePetWithForm(Long petId, String name, String status) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see PetApi#uploadFile */ default ResponseEntity uploadFile(Long petId, String additionalMetadata, @Valid MultipartFile file) { getRequest().ifPresent(request -> { for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { ApiUtil.setExampleResponse(request, "application/json", "{ \"code\" : 0, \"type\" : \"type\", \"message\" : \"message\"}"); break; } } }); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } default ResponseEntity> getAllPets(@NotNull Pageable pageable) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/PetApiDelegateImpl.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.io.File; import java.io.FileOutputStream; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import jakarta.annotation.PostConstruct; import jakarta.validation.constraints.NotNull; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springdoc.demo.app2.model.Category; import org.springdoc.demo.app2.model.ModelApiResponse; import org.springdoc.demo.app2.model.Pet; import org.springdoc.demo.app2.model.Tag; import org.springdoc.demo.app2.repository.PetRepository; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.server.ResponseStatusException; @Service public class PetApiDelegateImpl implements PetApiDelegate { private final PetRepository petRepository; private final NativeWebRequest request; public PetApiDelegateImpl(PetRepository petRepository, NativeWebRequest request) { this.petRepository = petRepository; this.request = request; } private static Pet createPet(long id, Category category, String name, String[] urls, String[] tags, Pet.StatusEnum status) { Pet pet = new Pet().id(id).category(category).name(name).status(status); if (null != urls) { pet.setPhotoUrls(Arrays.asList(urls)); } final AtomicLong i = new AtomicLong(0); if (null != tags && tags.length > 0) { Arrays.stream(tags).map(tag -> new Tag().name(tag).id(i.incrementAndGet())).forEach(pet::addTagsItem); } return pet; } @PostConstruct public void initPets() { Category dogs = new Category().id(1L).name("Dogs"); Category cats = new Category().id(2L).name("Cats"); Category rabbits = new Category().id(3L).name("Rabbits"); Category lions = new Category().id(4L).name("Lions"); petRepository.save(createPet(1, cats, "Cat 1", new String[] { "url1", "url2" }, new String[] { "tag1", "tag2" }, Pet.StatusEnum.AVAILABLE)); petRepository.save(createPet(2, cats, "Cat 2", new String[] { "url1", "url2" }, new String[] { "tag2", "tag3" }, Pet.StatusEnum.AVAILABLE)); petRepository.save(createPet(3, cats, "Cat 3", new String[] { "url1", "url2" }, new String[] { "tag3", "tag4" }, Pet.StatusEnum.PENDING)); petRepository.save(createPet(4, dogs, "Dog 1", new String[] { "url1", "url2" }, new String[] { "tag1", "tag2" }, Pet.StatusEnum.AVAILABLE)); petRepository.save(createPet(5, dogs, "Dog 2", new String[] { "url1", "url2" }, new String[] { "tag2", "tag3" }, Pet.StatusEnum.SOLD)); petRepository.save(createPet(6, dogs, "Dog 3", new String[] { "url1", "url2" }, new String[] { "tag3", "tag4" }, Pet.StatusEnum.PENDING)); petRepository.save(createPet(7, lions, "Lion 1", new String[] { "url1", "url2" }, new String[] { "tag1", "tag2" }, Pet.StatusEnum.AVAILABLE)); petRepository.save(createPet(8, lions, "Lion 2", new String[] { "url1", "url2" }, new String[] { "tag2", "tag3" }, Pet.StatusEnum.AVAILABLE)); petRepository.save(createPet(9, lions, "Lion 3", new String[] { "url1", "url2" }, new String[] { "tag3", "tag4" }, Pet.StatusEnum.AVAILABLE)); petRepository.save(createPet(10, rabbits, "Rabbit 1", new String[] { "url1", "url2" }, new String[] { "tag3", "tag4" }, Pet.StatusEnum.AVAILABLE)); } @Override public void addPet(Pet pet) { petRepository.save(pet); // return ResponseEntity.ok().build(); } @Override public ResponseEntity deletePet(Long petId, String apiKey) { petRepository.deleteById(petId); return ResponseEntity.ok().build(); } @Override public ResponseEntity> findPetsByStatus(List statusList) { List statusEnums = statusList.stream() .map(s -> Optional.ofNullable(Pet.StatusEnum.fromValue(s)) .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid status: " + s))) .collect(Collectors.toList()); return ResponseEntity.ok(petRepository.findPetsByStatus(statusEnums)); } @Override public ResponseEntity> findPetsByTags(List tags) { return ResponseEntity.ok(petRepository.findPetsByTags(tags)); } @Override public ResponseEntity getPetById(Long petId) { ApiUtil.checkApiKey(request); return petRepository.findById(petId).map(ResponseEntity::ok) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); } @Override public ResponseEntity updatePet(Pet pet) { return null; // return addPet(pet); } @Override public ResponseEntity updatePetWithForm(Long petId, String name, String status) { Pet pet = petRepository.findById(petId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); if (!StringUtils.isEmpty(name)) pet.name(name); if (!StringUtils.isEmpty(name)) pet.setStatus(Pet.StatusEnum.fromValue(status)); // return addPet(pet); return null; } @Override public ResponseEntity uploadFile(Long petId, String additionalMetadata, MultipartFile file) { try { String uploadedFileLocation = "./" + file.getName(); System.out.println("uploading to " + uploadedFileLocation); IOUtils.copy(file.getInputStream(), new FileOutputStream(uploadedFileLocation)); String msg = String.format("additionalMetadata: %s\nFile uploaded to %s, %d bytes", additionalMetadata, uploadedFileLocation, (new File(uploadedFileLocation)).length()); ModelApiResponse output = new ModelApiResponse().code(200).message(msg); return ResponseEntity.ok(output); } catch (Exception e) { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Couldn't upload file", e); } } public ResponseEntity> getAllPets(@NotNull Pageable pageable) { ApiUtil.checkApiKey(request); return new ResponseEntity>(petRepository.findAll(pageable), HttpStatus.OK); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/StoreApi.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ /** * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (3.0.0). * https://openapi-generator.tech * Do not edit the class manually. */ package org.springdoc.demo.app2.api; import java.util.Map; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import org.springdoc.demo.app2.model.Order; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @Tag(name = "store", description = "the store API") public interface StoreApi { default StoreApiDelegate getDelegate() { return new StoreApiDelegate() { }; } @Operation(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", tags = { "store" }) @ApiResponses(value = { @ApiResponse(responseCode = "400", description = "Invalid ID supplied"), @ApiResponse(responseCode = "404", description = "Order not found") }) @DeleteMapping(value = "/store/order/{orderId}") default ResponseEntity deleteOrder( @Parameter(description = "ID of the order that needs to be deleted", required = true, schema = @Schema(type = "integer", format = "int64")) @PathVariable("orderId") Long orderId) { return getDelegate().deleteOrder(orderId); } @Operation(summary = "Returns pet inventories by status", description = "Returns a map of status codes to quantities", security = { @SecurityRequirement(name = "api_key") }, tags = { "store" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(type = "object"))) }) @GetMapping(value = "/store/inventory", produces = { "application/json" }) default ResponseEntity> getInventory() { return getDelegate().getInventory(); } @Operation(summary = "Find purchase order by ID", description = "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", tags = { "store" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(implementation = Order.class))), @ApiResponse(responseCode = "400", description = "Invalid ID supplied", content = @Content), @ApiResponse(responseCode = "404", description = "Order not found", content = @Content) }) @GetMapping(value = "/store/order/{orderId}", produces = { "application/xml", "application/json" }) default ResponseEntity getOrderById( @Min(1L) @Max(5L) @Parameter(description = "ID of order that needs to be fetched", required = true) @PathVariable("orderId") Long orderId) { return getDelegate().getOrderById(orderId); } @Operation(summary = "Place an order for a pet", description = "Place a new order in the store", tags = { "store" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(implementation = Order.class))), @ApiResponse(responseCode = "405", description = "Invalid input", content = @Content) }) @PostMapping(value = "/store/order", produces = { "application/json" }, consumes = { "application/xml", "application/json", "application/x-www-form-urlencoded" }) default ResponseEntity placeOrder( @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody Order order) { return getDelegate().placeOrder(order); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/StoreApiController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.Optional; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("${openapi.openAPIPetstore.base-path:/}") public class StoreApiController implements StoreApi { private final StoreApiDelegate delegate; public StoreApiController(@org.springframework.beans.factory.annotation.Autowired(required = false) StoreApiDelegate delegate) { this.delegate = Optional.ofNullable(delegate).orElse(new StoreApiDelegate() { }); } @Override public StoreApiDelegate getDelegate() { return delegate; } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/StoreApiDelegate.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.Map; import java.util.Optional; import org.springdoc.demo.app2.model.Order; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.NativeWebRequest; /** * A delegate to be called by the {@link StoreApiController}}. * Implement this interface with a {@link org.springframework.stereotype.Service} annotated class. */ public interface StoreApiDelegate { default Optional getRequest() { return Optional.empty(); } /** * @see StoreApi#deleteOrder */ default ResponseEntity deleteOrder(Long orderId) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see StoreApi#getInventory */ default ResponseEntity> getInventory() { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see StoreApi#getOrderById */ default ResponseEntity getOrderById(Long orderId) { extract(); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } default void extract() { getRequest().ifPresent(request -> { for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { ApiUtil.setExampleResponse(request, "application/json", "{ \"petId\" : 6, \"quantity\" : 1, \"id\" : 0, \"shipDate\" : \"2000-01-23T04:56:07.000+00:00\", \"complete\" : false, \"status\" : \"placed\"}"); break; } if (mediaType.isCompatibleWith(MediaType.valueOf("application/xml"))) { ApiUtil.setExampleResponse(request, "application/xml", " 123456789 123456789 123 2000-01-23T04:56:07.000Z aeiou true"); break; } } }); } /** * @see StoreApi#placeOrder */ default ResponseEntity placeOrder(Order order) { extract(); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/StoreApiDelegateImpl.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.Date; import java.util.Map; import jakarta.annotation.PostConstruct; import org.springdoc.demo.app2.model.Order; import org.springdoc.demo.app2.repository.OrderRepository; import org.springdoc.demo.app2.repository.PetRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.server.ResponseStatusException; @Service public class StoreApiDelegateImpl implements StoreApiDelegate { private final OrderRepository orderRepository; @SuppressWarnings("unused") private final PetRepository petRepository; private final NativeWebRequest request; public StoreApiDelegateImpl(OrderRepository orderRepository, PetRepository petRepository, NativeWebRequest request) { this.orderRepository = orderRepository; this.petRepository = petRepository; this.request = request; } private static Order createOrder(long id, long petId, Order.StatusEnum status) { return new Order() .id(id) .petId(petId) .quantity(2) .shipDate(new Date()) .status(status); } @PostConstruct public void initOrders() { orderRepository.save(createOrder(1, 1, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(2, 1, Order.StatusEnum.DELIVERED)); orderRepository.save(createOrder(3, 2, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(4, 2, Order.StatusEnum.DELIVERED)); orderRepository.save(createOrder(5, 3, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(6, 3, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(7, 3, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(8, 3, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(9, 3, Order.StatusEnum.PLACED)); orderRepository.save(createOrder(10, 3, Order.StatusEnum.PLACED)); } @Override public ResponseEntity deleteOrder(Long orderId) { Order order = orderRepository.findById(orderId) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); orderRepository.delete(order); return ResponseEntity.ok().build(); } @Override public ResponseEntity> getInventory() { ApiUtil.checkApiKey(request); return ResponseEntity.ok().build(); } @Override public ResponseEntity getOrderById(Long orderId) { return orderRepository.findById(orderId) .map(ResponseEntity::ok) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Order not found")); } @Override public ResponseEntity placeOrder(Order order) { return ResponseEntity.ok(orderRepository.save(order)); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/UserApi.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ /** * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (3.0.0). * https://openapi-generator.tech * Do not edit the class manually. */ package org.springdoc.demo.app2.api; import java.util.List; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.Explode; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.enums.ParameterStyle; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springdoc.demo.app2.model.User; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @Tag(name = "user", description = "the user API") public interface UserApi { default UserApiDelegate getDelegate() { return new UserApiDelegate() { }; } @Operation(summary = "Create user", description = "This can only be done by the logged in user.", tags = { "user" }) @ApiResponses(value = { @ApiResponse(description = "successful operation", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = User.class)), @Content(mediaType = "application/xml", schema = @Schema(implementation = User.class)) }) }) @PostMapping(value = "/user", consumes = { "application/json", "application/xml", "application/x-www-form-urlencoded" }) default ResponseEntity createUser( @Parameter(description = "Created user object") @Valid @RequestBody User user) { return getDelegate().createUser(user); } @Operation(summary = "Creates list of users with given input array", tags = { "user" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation") }) @PostMapping(value = "/user/createWithArray", consumes = { "application/json" }) default ResponseEntity createUsersWithArrayInput( @Parameter(description = "List of user object", required = true) @Valid @RequestBody List user) { return getDelegate().createUsersWithArrayInput(user); } @Operation(summary = "Creates list of users with given input array", description = "Creates list of users with given input array", tags = { "user" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successful operation", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = User.class)), @Content(mediaType = "application/xml", schema = @Schema(implementation = User.class)) }), @ApiResponse(description = "successful operation") }) @PostMapping(value = "/user/createWithList", consumes = { "application/json" }) default ResponseEntity createUsersWithListInput( @Parameter @Valid @RequestBody List user) { return getDelegate().createUsersWithListInput(user); } @Operation(summary = "Delete user", description = "This can only be done by the logged in user.", tags = { "user" }) @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "User deleted"), @ApiResponse(responseCode = "400", description = "Invalid username supplied"), @ApiResponse(responseCode = "404", description = "User not found") }) @DeleteMapping(value = "/user/{username}") default ResponseEntity deleteUser( @Parameter(description = "The name that needs to be deleted", required = true) @PathVariable("username") String username) { return getDelegate().deleteUser(username); } @Operation(summary = "Get user by user name", tags = { "user" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "successful operation", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = User.class)), @Content(mediaType = "application/xml", schema = @Schema(implementation = User.class)) }), @ApiResponse(responseCode = "400", description = "Invalid username supplied", content = @Content), @ApiResponse(responseCode = "404", description = "User not found", content = @Content) }) @GetMapping(value = "/user/{username}") default ResponseEntity getUserByName( @Parameter(description = "The name that needs to be fetched. Use user1 for testing. ", required = true) @PathVariable("username") String username) { return getDelegate().getUserByName(username); } @Operation(summary = "Logs user into the system", tags = { "user" }) @ApiResponses(value = { @ApiResponse(responseCode = "200", headers = { @Header(name = "X-Rate-Limit", description = "calls per hour allowed by the user", schema = @Schema(type = "integer", format = "int32")), @Header(name = "X-Expires-After", description = "date in UTC when toekn expires", schema = @Schema(type = "string", format = "date-time")) }, description = "successful operation", content = @Content(schema = @Schema(implementation = String.class))), @ApiResponse(responseCode = "400", description = "Invalid username/password supplied", content = @Content) }) @GetMapping(value = "/user/login", produces = { "application/xml", "application/json" }) default ResponseEntity loginUser( @NotNull @Parameter(description = "The user name for login", required = false) @Valid @RequestParam(value = "username", required = false) String username, @NotNull @Parameter(description = "The password for login in clear text", required = false) @Valid @RequestParam(value = "password", required = false) String password) { return getDelegate().loginUser(username, password); } @Operation(summary = "Logs out current logged in user session", tags = { "user" }) @ApiResponses(value = { @ApiResponse(description = "successful operation") }) @GetMapping(value = "/user/logout") default ResponseEntity logoutUser() { return getDelegate().logoutUser(); } @Operation(summary = "Update user", description = "This can only be done by the logged in user.", tags = { "user" }) @ApiResponses(value = @ApiResponse(description = "successful operation")) @PutMapping(value = "/user/{username}", consumes = { "application/json", "application/xml", "application/x-www-form-urlencoded" }) default ResponseEntity updateUser( @Parameter(description = "name that need to be deleted", required = true, explode = Explode.FALSE, in = ParameterIn.PATH, name = "username", style = ParameterStyle.SIMPLE, schema = @Schema(type = "string")) @PathVariable("username") String username, @Parameter(description = "Update an existent user in the store") @Valid @RequestBody User user) { return getDelegate().updateUser(username, user); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/UserApiController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.Optional; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("${openapi.openAPIPetstore.base-path:/}") public class UserApiController implements UserApi { private final UserApiDelegate delegate; public UserApiController(@org.springframework.beans.factory.annotation.Autowired(required = false) UserApiDelegate delegate) { this.delegate = Optional.ofNullable(delegate).orElse(new UserApiDelegate() { }); } @Override public UserApiDelegate getDelegate() { return delegate; } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/UserApiDelegate.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.util.List; import java.util.Optional; import org.springdoc.demo.app2.model.User; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.NativeWebRequest; /** * A delegate to be called by the {@link UserApiController}}. * Implement this interface with a {@link org.springframework.stereotype.Service} annotated class. */ public interface UserApiDelegate { default Optional getRequest() { return Optional.empty(); } /** * @see UserApi#createUser */ default ResponseEntity createUser(User user) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#createUsersWithArrayInput */ default ResponseEntity createUsersWithArrayInput(List user) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#createUsersWithListInput */ default ResponseEntity createUsersWithListInput(List user) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#deleteUser */ default ResponseEntity deleteUser(String username) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#getUserByName */ default ResponseEntity getUserByName(String username) { getRequest().ifPresent(request -> { for (MediaType mediaType : MediaType.parseMediaTypes(request.getHeader("Accept"))) { if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { ApiUtil.setExampleResponse(request, "application/json", "{ \"firstName\" : \"firstName\", \"lastName\" : \"lastName\", \"password\" : \"password\", \"userStatus\" : 6, \"phone\" : \"phone\", \"id\" : 0, \"email\" : \"email\", \"username\" : \"username\"}"); break; } if (mediaType.isCompatibleWith(MediaType.valueOf("application/xml"))) { ApiUtil.setExampleResponse(request, "application/xml", " 123456789 aeiou aeiou aeiou aeiou aeiou aeiou 123"); break; } } }); return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#loginUser */ default ResponseEntity loginUser(String username, String password) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#logoutUser */ default ResponseEntity logoutUser() { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } /** * @see UserApi#updateUser */ default ResponseEntity updateUser(String username, User user) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/api/UserApiDelegateImpl.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.api; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.List; import jakarta.annotation.PostConstruct; import org.springdoc.demo.app2.model.User; import org.springdoc.demo.app2.repository.UserRepository; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; @Service public class UserApiDelegateImpl implements UserApiDelegate { private final UserRepository userRepository; public UserApiDelegateImpl(UserRepository userRepository) { this.userRepository = userRepository; } private static User createUser(long id, String username, String firstName, String lastName, String email, int userStatus) { return new User() .id(id) .username(username) .firstName(firstName) .lastName(lastName) .email(email) .password("XXXXXXXXXXX") .phone("123-456-7890") .userStatus(userStatus); } @PostConstruct public void initUsers() { userRepository.save(createUser(1, "user1", "first name 1", "last name 1", "email1@test.com", 1)); userRepository.save(createUser(2, "user2", "first name 2", "last name 2", "email2@test.com", 2)); userRepository.save(createUser(3, "user3", "first name 3", "last name 3", "email3@test.com", 3)); userRepository.save(createUser(4, "user4", "first name 4", "last name 4", "email4@test.com", 1)); userRepository.save(createUser(5, "user5", "first name 5", "last name 5", "email5@test.com", 2)); userRepository.save(createUser(6, "user6", "first name 6", "last name 6", "email6@test.com", 3)); userRepository.save(createUser(7, "user7", "first name 7", "last name 7", "email7@test.com", 1)); userRepository.save(createUser(8, "user8", "first name 8", "last name 8", "email8@test.com", 2)); userRepository.save(createUser(9, "user9", "first name 9", "last name 9", "email9@test.com", 3)); userRepository.save(createUser(10, "user10", "first name 10", "last name 10", "email10@test.com", 1)); userRepository.save(createUser(11, "user?10", "first name ?10", "last name ?10", "email101@test.com", 1)); } @Override public ResponseEntity createUser(User user) { userRepository.save(user); return ResponseEntity.ok().build(); } @Override public ResponseEntity createUsersWithArrayInput(List users) { userRepository.saveAll(users); return ResponseEntity.ok().build(); } @Override public ResponseEntity createUsersWithListInput(List users) { return createUsersWithArrayInput(users); } @Override public ResponseEntity deleteUser(String username) { User user = userRepository.findById(username) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); userRepository.delete(user); return ResponseEntity.noContent().build(); } @Override public ResponseEntity getUserByName(String username) { return userRepository.findById(username) .map(ResponseEntity::ok) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); } @Override public ResponseEntity loginUser(String username, String password) { Instant now = Instant.now().plus(1, ChronoUnit.HOURS); return ResponseEntity.ok() .header("X-Expires-After", new Date(now.toEpochMilli()).toString()) .header("X-Rate-Limit", "5000") .body("logged in user session:" + now.toEpochMilli()); } @Override public ResponseEntity logoutUser() { return ResponseEntity.ok().build(); } @Override public ResponseEntity updateUser(String username, User user) { user.setUsername(username); return createUser(user); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/Body.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; public class Body { @Schema(description = "Updated name of the pet") /** * Updated name of the pet **/ private String name = null; @Schema(description = "Updated status of the pet") /** * Updated status of the pet **/ private String status = null; /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private static String toIndentedString(java.lang.Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } /** * Updated name of the pet * * @return name **/ @JsonProperty("name") public String getName() { return name; } public void setName(String name) { this.name = name; } public Body name(String name) { this.name = name; return this; } /** * Updated status of the pet * * @return status **/ @JsonProperty("status") public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Body status(String status) { this.status = status; return this; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Body {\n"); sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" status: ").append(toIndentedString(status)).append("\n"); sb.append("}"); return sb.toString(); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/Body1.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.io.File; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; public class Body1 { @Schema(description = "Additional data to pass to server") /** * Additional data to pass to server **/ private String additionalMetadata = null; @Schema(description = "file to upload") /** * file to upload **/ private File file = null; /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private static String toIndentedString(java.lang.Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } /** * Additional data to pass to server * * @return additionalMetadata **/ @JsonProperty("additionalMetadata") public String getAdditionalMetadata() { return additionalMetadata; } public void setAdditionalMetadata(String additionalMetadata) { this.additionalMetadata = additionalMetadata; } public Body1 additionalMetadata(String additionalMetadata) { this.additionalMetadata = additionalMetadata; return this; } /** * file to upload * * @return file **/ @JsonProperty("file") public File getFile() { return file; } public void setFile(File file) { this.file = file; } public Body1 file(File file) { this.file = file; return this; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Body1 {\n"); sb.append(" additionalMetadata: ").append(toIndentedString(additionalMetadata)).append("\n"); sb.append(" file: ").append(toIndentedString(file)).append("\n"); sb.append("}"); return sb.toString(); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/Category.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; /** * Category */ @JacksonXmlRootElement(localName = "category") @XmlRootElement(name = "category") @XmlAccessorType(XmlAccessType.FIELD) public class Category { @JsonProperty("id") @JacksonXmlProperty(localName = "id") private Long id; @JsonProperty("name") @JacksonXmlProperty(localName = "name") private String name; public Category id(Long id) { this.id = id; return this; } /** * Get id * * @return id */ @Schema(example = "1", description = "") public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Category name(String name) { this.name = name; return this; } /** * Get name * * @return name */ @Schema(example = "Dogs", description = "") public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Category category = (Category) o; return Objects.equals(this.id, category.id) && Objects.equals(this.name, category.name); } @Override public int hashCode() { return Objects.hash(id, name); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Category {\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append("}"); return sb.toString(); } /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private String toIndentedString(Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/ModelApiResponse.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; /** * ModelApiResponse */ @JacksonXmlRootElement(localName = "##default") @XmlRootElement(name = "##default") @XmlAccessorType(XmlAccessType.FIELD) public class ModelApiResponse { @JsonProperty("code") @JacksonXmlProperty(localName = "code") private Integer code; @JsonProperty("type") @JacksonXmlProperty(localName = "type") private String type; @JsonProperty("message") @JacksonXmlProperty(localName = "message") private String message; public ModelApiResponse code(Integer code) { this.code = code; return this; } /** * Get code * * @return code */ @Schema(description = "") public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public ModelApiResponse type(String type) { this.type = type; return this; } /** * Get type * * @return type */ @Schema(description = "") public String getType() { return type; } public void setType(String type) { this.type = type; } public ModelApiResponse message(String message) { this.message = message; return this; } /** * Get message * * @return message */ @Schema(description = "") public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ModelApiResponse modelApiResponse = (ModelApiResponse) o; return Objects.equals(this.code, modelApiResponse.code) && Objects.equals(this.type, modelApiResponse.type) && Objects.equals(this.message, modelApiResponse.message); } @Override public int hashCode() { return Objects.hash(code, type, message); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class ModelApiResponse {\n"); sb.append(" code: ").append(toIndentedString(code)).append("\n"); sb.append(" type: ").append(toIndentedString(type)).append("\n"); sb.append(" message: ").append(toIndentedString(message)).append("\n"); sb.append("}"); return sb.toString(); } /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private String toIndentedString(Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/Order.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.util.Date; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; /** * Order */ @JacksonXmlRootElement(localName = "order") @XmlRootElement(name = "order") @XmlAccessorType(XmlAccessType.FIELD) public class Order { @JsonProperty("id") @JacksonXmlProperty(localName = "id") private Long id; @JsonProperty("petId") @JacksonXmlProperty(localName = "petId") private Long petId; @JsonProperty("quantity") @JacksonXmlProperty(localName = "quantity") private Integer quantity; @JsonProperty("shipDate") @JacksonXmlProperty(localName = "shipDate") private Date shipDate; @JsonProperty("status") @JacksonXmlProperty(localName = "status") private StatusEnum status; @JsonProperty("complete") @JacksonXmlProperty(localName = "complete") private Boolean complete; public Order id(Long id) { this.id = id; return this; } /** * Get id * * @return id */ @Schema(example = "10", description = "") public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Order petId(Long petId) { this.petId = petId; return this; } /** * Get petId * * @return petId */ @Schema(example = "198772", description = "") public Long getPetId() { return petId; } public void setPetId(Long petId) { this.petId = petId; } public Order quantity(Integer quantity) { this.quantity = quantity; return this; } /** * Get quantity * * @return quantity */ @Schema(example = "7", description = "") public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } public Order shipDate(Date shipDate) { this.shipDate = shipDate; return this; } /** * Get shipDate * * @return shipDate */ @Schema(description = "") @Valid public Date getShipDate() { return shipDate; } public void setShipDate(Date shipDate) { this.shipDate = shipDate; } public Order status(StatusEnum status) { this.status = status; return this; } /** * Order Status * * @return status */ @Schema(example = "approved", description = "Order Status") public StatusEnum getStatus() { return status; } public void setStatus(StatusEnum status) { this.status = status; } public Order complete(Boolean complete) { this.complete = complete; return this; } /** * Get complete * * @return complete */ @Schema(description = "") public Boolean getComplete() { return complete; } public void setComplete(Boolean complete) { this.complete = complete; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Order order = (Order) o; return Objects.equals(this.id, order.id) && Objects.equals(this.petId, order.petId) && Objects.equals(this.quantity, order.quantity) && Objects.equals(this.shipDate, order.shipDate) && Objects.equals(this.status, order.status) && Objects.equals(this.complete, order.complete); } @Override public int hashCode() { return Objects.hash(id, petId, quantity, shipDate, status, complete); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Order {\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" petId: ").append(toIndentedString(petId)).append("\n"); sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); sb.append(" shipDate: ").append(toIndentedString(shipDate)).append("\n"); sb.append(" status: ").append(toIndentedString(status)).append("\n"); sb.append(" complete: ").append(toIndentedString(complete)).append("\n"); sb.append("}"); return sb.toString(); } /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private String toIndentedString(Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } /** * Order Status */ public enum StatusEnum { PLACED("placed"), APPROVED("approved"), DELIVERED("delivered"); private String value; StatusEnum(String value) { this.value = value; } @JsonCreator public static StatusEnum fromValue(String value) { for (StatusEnum b : StatusEnum.values()) { if (b.value.equals(value)) { return b; } } throw new IllegalArgumentException("Unexpected value '" + value + "'"); } @Override @JsonValue public String toString() { return String.valueOf(value); } } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/Pet.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.util.ArrayList; import java.util.List; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; /** * Pet */ @JacksonXmlRootElement(localName = "pet") @XmlRootElement(name = "pet") @XmlAccessorType(XmlAccessType.FIELD) public class Pet { @JsonProperty("id") @JacksonXmlProperty(localName = "id") private Long id; @JsonProperty("name") @JacksonXmlProperty(localName = "name") private String name; @JsonProperty("category") @JacksonXmlProperty(localName = "category") private Category category; @JsonProperty("photoUrls") @JacksonXmlProperty(localName = "photoUrls") @Valid private List photoUrls = new ArrayList(); @JsonProperty("tags") @JacksonXmlProperty(localName = "tags") @Valid private List tags = null; @JsonProperty("status") @JacksonXmlProperty(localName = "status") private StatusEnum status; public Pet id(Long id) { this.id = id; return this; } /** * Get id * * @return id */ @Schema(example = "10", description = "") public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Pet name(String name) { this.name = name; return this; } /** * Get name * * @return name */ @Schema(example = "doggie", required = true, description = "") @NotNull public String getName() { return name; } public void setName(String name) { this.name = name; } public Pet category(Category category) { this.category = category; return this; } /** * Get category * * @return category */ @Schema(description = "") @Valid public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public Pet photoUrls(List photoUrls) { this.photoUrls = photoUrls; return this; } public Pet addPhotoUrlsItem(String photoUrlsItem) { this.photoUrls.add(photoUrlsItem); return this; } /** * Get photoUrls * * @return photoUrls */ @Schema(required = true, description = "") @NotNull public List getPhotoUrls() { return photoUrls; } public void setPhotoUrls(List photoUrls) { this.photoUrls = photoUrls; } public Pet tags(List tags) { this.tags = tags; return this; } public Pet addTagsItem(Tag tagsItem) { if (this.tags == null) { this.tags = new ArrayList<>(); } this.tags.add(tagsItem); return this; } /** * Get tags * * @return tags */ @Schema(description = "") @Valid public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } public Pet status(StatusEnum status) { this.status = status; return this; } /** * pet status in the store * * @return status */ @Schema(description = "pet status in the store") public StatusEnum getStatus() { return status; } public void setStatus(StatusEnum status) { this.status = status; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Pet pet = (Pet) o; return Objects.equals(this.id, pet.id) && Objects.equals(this.name, pet.name) && Objects.equals(this.category, pet.category) && Objects.equals(this.photoUrls, pet.photoUrls) && Objects.equals(this.tags, pet.tags) && Objects.equals(this.status, pet.status); } @Override public int hashCode() { return Objects.hash(id, name, category, photoUrls, tags, status); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Pet {\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append(" category: ").append(toIndentedString(category)).append("\n"); sb.append(" photoUrls: ").append(toIndentedString(photoUrls)).append("\n"); sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); sb.append(" status: ").append(toIndentedString(status)).append("\n"); sb.append("}"); return sb.toString(); } /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private String toIndentedString(Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } /** * pet status in the store */ public enum StatusEnum { AVAILABLE("available"), PENDING("pending"), SOLD("sold"); private String value; StatusEnum(String value) { this.value = value; } @JsonCreator public static StatusEnum fromValue(String value) { for (StatusEnum b : StatusEnum.values()) { if (b.value.equals(value)) { return b; } } throw new IllegalArgumentException("Unexpected value '" + value + "'"); } @Override @JsonValue public String toString() { return String.valueOf(value); } } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/Tag.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; /** * Tag */ @JacksonXmlRootElement(localName = "tag") @XmlRootElement(name = "tag") @XmlAccessorType(XmlAccessType.FIELD) public class Tag { @JsonProperty("id") @JacksonXmlProperty(localName = "id") private Long id; @JsonProperty("name") @JacksonXmlProperty(localName = "name") private String name; public Tag id(Long id) { this.id = id; return this; } /** * Get id * * @return id */ @Schema(description = "") public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Tag name(String name) { this.name = name; return this; } /** * Get name * * @return name */ @Schema(description = "") public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Tag tag = (Tag) o; return Objects.equals(this.id, tag.id) && Objects.equals(this.name, tag.name); } @Override public int hashCode() { return Objects.hash(id, name); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class Tag {\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" name: ").append(toIndentedString(name)).append("\n"); sb.append("}"); return sb.toString(); } /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private String toIndentedString(Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/model/User.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.model; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; /** * User */ @JacksonXmlRootElement(localName = "user") @XmlRootElement(name = "user") @XmlAccessorType(XmlAccessType.FIELD) public class User { @JsonProperty("id") @JacksonXmlProperty(localName = "id") private Long id; @JsonProperty("username") @JacksonXmlProperty(localName = "username") private String username; @JsonProperty("firstName") @JacksonXmlProperty(localName = "firstName") private String firstName; @JsonProperty("lastName") @JacksonXmlProperty(localName = "lastName") private String lastName; @JsonProperty("email") @JacksonXmlProperty(localName = "email") private String email; @JsonProperty("password") @JacksonXmlProperty(localName = "password") private String password; @JsonProperty("phone") @JacksonXmlProperty(localName = "phone") private String phone; @JsonProperty("userStatus") @JacksonXmlProperty(localName = "userStatus") private Integer userStatus; public User id(Long id) { this.id = id; return this; } /** * Get id * * @return id */ @Schema(example = "10", description = "") public Long getId() { return id; } public void setId(Long id) { this.id = id; } public User username(String username) { this.username = username; return this; } /** * Get username * * @return username */ @Schema(example = "theUser", description = "") public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public User firstName(String firstName) { this.firstName = firstName; return this; } /** * Get firstName * * @return firstName */ @Schema(example = "John", description = "") public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public User lastName(String lastName) { this.lastName = lastName; return this; } /** * Get lastName * * @return lastName */ @Schema(example = "James", description = "") public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public User email(String email) { this.email = email; return this; } /** * Get email * * @return email */ @Schema(example = "john@email.com", description = "") public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public User password(String password) { this.password = password; return this; } /** * Get password * * @return password */ @Schema(example = "12345", description = "") public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public User phone(String phone) { this.phone = phone; return this; } /** * Get phone * * @return phone */ @Schema(example = "12345", description = "") public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public User userStatus(Integer userStatus) { this.userStatus = userStatus; return this; } /** * User Status * * @return userStatus */ @Schema(example = "1", description = "User Status") public Integer getUserStatus() { return userStatus; } public void setUserStatus(Integer userStatus) { this.userStatus = userStatus; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } User user = (User) o; return Objects.equals(this.id, user.id) && Objects.equals(this.username, user.username) && Objects.equals(this.firstName, user.firstName) && Objects.equals(this.lastName, user.lastName) && Objects.equals(this.email, user.email) && Objects.equals(this.password, user.password) && Objects.equals(this.phone, user.phone) && Objects.equals(this.userStatus, user.userStatus); } @Override public int hashCode() { return Objects.hash(id, username, firstName, lastName, email, password, phone, userStatus); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class User {\n"); sb.append(" id: ").append(toIndentedString(id)).append("\n"); sb.append(" username: ").append(toIndentedString(username)).append("\n"); sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n"); sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n"); sb.append(" email: ").append(toIndentedString(email)).append("\n"); sb.append(" password: ").append(toIndentedString(password)).append("\n"); sb.append(" phone: ").append(toIndentedString(phone)).append("\n"); sb.append(" userStatus: ").append(toIndentedString(userStatus)).append("\n"); sb.append("}"); return sb.toString(); } /** * Convert the given object to string with each line indented by 4 spaces * (except the first line). */ private String toIndentedString(Object o) { if (o == null) { return "null"; } return o.toString().replace("\n", "\n "); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/repository/HashMapRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.repository; import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.util.Assert; @NoRepositoryBean public abstract class HashMapRepository implements CrudRepository { private final BeanWrapper entityBeanInfo; Map entities = new HashMap<>(); protected HashMapRepository(Class clazz) { entityBeanInfo = new BeanWrapperImpl(clazz); } abstract ID getEntityId(S entity); @Override public S save(S entity) { Assert.notNull(entity, "entity cannot be null"); Assert.notNull(getEntityId(entity), "entity ID cannot be null"); entities.put(getEntityId(entity), entity); return entity; } @Override public List saveAll(Iterable entities) { Assert.notNull(entities, "entities cannot be null"); List result = new ArrayList<>(); entities.forEach(entity -> result.add(save(entity))); return result; } @Override public Collection findAll() { return entities.values(); } public List findAll(Pageable pageable) { final List result; final Sort sort = pageable.getSort(); Comparator comp = new Comparator() { @Override public int compare(T t1, T t2) { int result = 0; for (Sort.Order o : sort) { final String prop = o.getProperty(); PropertyDescriptor propDesc = entityBeanInfo.getPropertyDescriptor(prop); result = ((Comparable) propDesc.createPropertyEditor(t1).getValue()) .compareTo((T) propDesc.createPropertyEditor(t2).getValue()); if (o.isDescending()) { result = -result; } if (result != 0) break; } return result; } }; final Set set = new TreeSet<>(comp); set.addAll(entities.values()); result = getPageSlice(pageable, set); return result; } private List getPageSlice(Pageable pageable, Collection col) { final ArrayList all = new ArrayList<>(col); final int size = all.size(); final int psize = pageable.getPageSize(); final int pnum = pageable.getPageNumber(); if (pnum < 1) { throw new IllegalArgumentException("page number must be 1 or more"); } if (psize < 1) { throw new IllegalArgumentException("page size must be 1 or more"); } // inclusive final int begin = (pnum - 1) * psize; // exclusive final int end = Math.min(begin + psize, size); if (size < begin) { return new ArrayList<>(); } // return of slice is valid because all is local to this method return all.subList(begin, end); } @Override public long count() { return entities.keySet().size(); } @Override public void delete(T entity) { Assert.notNull(entity, "entity cannot be null"); deleteById(getEntityId(entity)); } @Override public void deleteAll(Iterable entitiesToDelete) { Assert.notNull(entitiesToDelete, "entities cannot be null"); entitiesToDelete.forEach(entity -> entities.remove(getEntityId(entity))); } @Override public void deleteAll() { entities.clear(); } @Override public void deleteById(ID id) { Assert.notNull(id, "Id cannot be null"); entities.remove(id); } @Override public List findAllById(Iterable ids) { Assert.notNull(ids, "Ids cannot be null"); List result = new ArrayList<>(); ids.forEach(id -> findById(id).ifPresent(result::add)); return result; } @Override public boolean existsById(ID id) { Assert.notNull(id, "Id cannot be null"); return entities.containsKey(id); } public T findOne(ID id) { Assert.notNull(id, "Id cannot be null"); return entities.get(id); } @Override public Optional findById(ID id) { return Optional.ofNullable(findOne(id)); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/repository/OrderRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.repository; import org.springdoc.demo.app2.model.Order; import org.springframework.stereotype.Repository; @Repository public class OrderRepository extends HashMapRepository { public OrderRepository() { super(Order.class); } @Override Long getEntityId(S order) { return order.getId(); } @Override public void deleteAllById(Iterable iterable) { } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/repository/PetRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.repository; import java.util.List; import java.util.stream.Collectors; import org.springdoc.demo.app2.model.Pet; import org.springdoc.demo.app2.model.Tag; import org.springframework.stereotype.Repository; @Repository public class PetRepository extends HashMapRepository { private Long sequenceId = 1L; public PetRepository() { super(Pet.class); } @Override Long getEntityId(S pet) { return pet.getId(); } @Override public S save(S pet) { if (pet.getId() != null && pet.getId() > sequenceId) { sequenceId = pet.getId() + 1; } if (pet.getId() == null) { pet.setId(sequenceId); sequenceId += 1; } return super.save(pet); } @Override public void deleteAllById(Iterable iterable) { } public List findPetsByStatus(List statusList) { return entities.values().stream() .filter(entity -> entity.getStatus() != null) .filter(entity -> statusList.contains(entity.getStatus())) .collect(Collectors.toList()); } public List findPetsByTags(List tags) { return entities.values().stream() .filter(entity -> entity.getTags() != null) .filter(entity -> entity.getTags().stream() .map(Tag::getName) .anyMatch(tags::contains) ) .collect(Collectors.toList()); } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/java/org/springdoc/demo/app2/repository/UserRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app2.repository; import org.springdoc.demo.app2.model.User; import org.springframework.stereotype.Repository; @Repository public class UserRepository extends HashMapRepository { public UserRepository() { super(User.class); } @Override String getEntityId(S user) { return user.getUsername(); } @Override public void deleteAllById(Iterable iterable) { } } ================================================ FILE: demo-spring-boot-3-webmvc/src/main/resources/application.yml ================================================ management: endpoints: web: base-path: /rest/actuator exposure: include: '*' springdoc: version: '@springdoc.version@' swagger-ui: display-request-duration: true groups-order: DESC operationsSorter: method disable-swagger-default-url: true use-root-path: true show-actuator: true group-configs: - group: stores paths-to-match: /store/** cache: disabled: true server: port: 8081 forward-headers-strategy: framework ================================================ FILE: demo-spring-boot-3-webmvc/src/main/resources/petstore.yml ================================================ 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 Swagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! You can now help us improve the API whether it's by making changes to the definition itself or to the code. That way, with time, we can improve the API in general, and expose some of the new features in OAS3. Some useful links: - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) - [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.4 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/ModelApiResponse' 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 toekn 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 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 ModelApiResponse: 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: demo-spring-boot-webflux-scalar/pom.xml ================================================ 4.0.0 org.springdoc springdoc-openapi-demos 3.1.7-SNAPSHOT demo-spring-boot-webflux-scalar Demo Spring-boot Webflux Scalar org.springdoc springdoc-openapi-starter-webflux-scalar org.springframework.boot spring-boot-starter-webflux io.projectreactor reactor-test test org.springframework.boot spring-boot-starter-data-mongodb-reactive org.springframework.boot spring-boot-configuration-processor true org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/WebfluxDemoApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; @SpringBootApplication @EnableReactiveMongoRepositories public class WebfluxDemoApplication { public static void main(String[] args) { SpringApplication.run(WebfluxDemoApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components().addSecuritySchemes("basicScheme", new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic"))) .info(new Info().title("Tweet API").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } @Bean public GroupedOpenApi storeOpenApi() { String[] paths = { "/tweets/**" }; return GroupedOpenApi.builder().group("tweets").pathsToMatch(paths) .build(); } @Bean public GroupedOpenApi userOpenApi() { String[] paths = { "/stream/**" }; String[] packagedToMatch = { "org.springdoc.demo.app3" }; return GroupedOpenApi.builder().group("x-stream").pathsToMatch(paths).packagesToScan(packagedToMatch) .build(); } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/controller/ExceptionTranslator.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.controller; import org.springdoc.demo.app3.exception.TweetNotFoundException; import org.springdoc.demo.app3.payload.ErrorResponse; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class ExceptionTranslator { @SuppressWarnings("rawtypes") @ExceptionHandler(DuplicateKeyException.class) @ResponseStatus(HttpStatus.CONFLICT) public ResponseEntity handleDuplicateKeyException(DuplicateKeyException ex) { return ResponseEntity.status(HttpStatus.CONFLICT) .body(new ErrorResponse("A Tweet with the same text already exists")); } @SuppressWarnings("rawtypes") @ExceptionHandler(TweetNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ResponseEntity handleTweetNotFoundException(TweetNotFoundException ex) { return ResponseEntity.notFound().build(); } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/controller/TweetController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import org.springdoc.demo.app3.dto.TweetDTO; import org.springdoc.demo.app3.model.Tweet; import org.springdoc.demo.app3.repository.TweetRepository; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; /** * Created by bnasslahsen */ @RestController public class TweetController { private final TweetRepository tweetRepository; private final TweetMapper tweetMapper; public TweetController(TweetRepository tweetRepository, TweetMapper tweetMapper) { this.tweetRepository = tweetRepository; this.tweetMapper = tweetMapper; } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "get All Tweets") }) @GetMapping("/tweets") public Flux getAllTweets() { Flux tweet = tweetRepository.findAll(); return tweetMapper.toDTO(tweet); } @PostMapping("/tweets") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "create Tweets") }) public Mono createTweets(@Valid @RequestBody TweetDTO tweetDTO) { return tweetRepository.save(tweetMapper.toEntity(tweetDTO)).map(tweetMapper::toDTO); } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "get Tweet By Id"), @ApiResponse(responseCode = "404", description = "tweet not found") }) @GetMapping("/tweets/{id}") public Mono> getTweetById(@PathVariable(value = "id") String tweetId) { return tweetRepository.findById(tweetId).map(savedTweet -> ResponseEntity.ok(tweetMapper.toDTO(savedTweet))) .defaultIfEmpty(ResponseEntity.notFound().build()); } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "update Tweet"), @ApiResponse(responseCode = "404", description = "tweet not found") }) @PutMapping("/tweets/{id}") public Mono> updateTweet(@PathVariable(value = "id") String tweetId, @Valid @RequestBody TweetDTO tweetDTO) { return tweetRepository.findById(tweetId).flatMap(existingTweet -> { existingTweet.setText(tweetMapper.toEntity(tweetDTO).getText()); return tweetRepository.save(existingTweet); }).map(updateTweet -> new ResponseEntity<>(tweetMapper.toDTO(updateTweet), HttpStatus.OK)) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "delete Tweet"), @ApiResponse(responseCode = "404", description = "tweet not found") }) @DeleteMapping("/tweets/{id}") public Mono> deleteTweet(@PathVariable(value = "id") String tweetId) { return tweetRepository.findById(tweetId) .flatMap(existingTweet -> tweetRepository.delete(existingTweet) .then(Mono.just(new ResponseEntity(HttpStatus.OK)))) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @Operation(description = "Tweets are Sent to the client as Server Sent Events", responses = { @ApiResponse(responseCode = "200", description = "stream All Tweets") }) @GetMapping(value = "/stream/tweets", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux streamAllTweets() { return tweetMapper.toDTO(tweetRepository.findAll()); } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/controller/TweetMapper.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.controller; import org.springdoc.demo.app3.dto.TweetDTO; import org.springdoc.demo.app3.model.Tweet; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; @Component public class TweetMapper { Flux toDTO(Flux tweet) { return tweet.map(this::toDTO); } Flux toEntity(Flux tweetDTO) { return tweetDTO.map(this::toEntity); } Mono toDTO(Mono tweet) { return tweet.map(this::toDTO); } Mono toEntity(Mono tweetDTO) { return tweetDTO.map(this::toEntity); } TweetDTO toDTO(Tweet tweet) { TweetDTO teTweetDTO = new TweetDTO(); BeanUtils.copyProperties(tweet,teTweetDTO); return teTweetDTO; } Tweet toEntity(TweetDTO tweetDTO) { Tweet teTweet = new Tweet(); BeanUtils.copyProperties(tweetDTO, teTweet); return teTweet; } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/dto/TweetDTO.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.dto; import java.util.Date; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; /** * Created by bnasslahsen on 08/09/17. */ public class TweetDTO { private String id; @NotBlank @Size(max = 140) private String text; @NotNull private Date createdAt = new Date(); public TweetDTO() { } public TweetDTO(String text) { this.text = text; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/exception/TweetNotFoundException.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.exception; /** * Created by bnasslahsen */ public class TweetNotFoundException extends RuntimeException { /** * */ private static final long serialVersionUID = 1L; public TweetNotFoundException(String tweetId) { super("Tweet not found with id " + tweetId); } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/model/Tweet.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.model; import java.util.Date; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; /** * Created by bnasslahsen on 08/09/17. */ @Document(collection = "tweets") public class Tweet { @Id private String id; @NotBlank @Size(max = 140) private String text; @NotNull private Date createdAt = new Date(); public Tweet() { } public Tweet(String text) { this.text = text; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/payload/ErrorResponse.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.payload; /** * Created by bnasslahsen */ public class ErrorResponse { private String message; public ErrorResponse(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/repository/TweetRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.app3.repository; import org.springdoc.demo.app3.model.Tweet; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.stereotype.Repository; /** * Created by bnasslahsen on 08/09/17. */ @Repository public interface TweetRepository extends ReactiveMongoRepository { } ================================================ FILE: demo-spring-boot-webflux-scalar/src/main/resources/application.yml ================================================ server: port: 8082 forward-headers-strategy: framework spring: data: mongodb: uri: ${SPRING_DATA_MONGODB_URI:mongodb://localhost:27017/tweetsdb} springdoc: version: '@springdoc.version@' ================================================ FILE: demo-spring-boot-webflux-scalar/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst ================================================ org/springdoc/demo/app3/dto/TweetDTO.class org/springdoc/demo/app3/controller/ExceptionTranslator.class org/springdoc/demo/app3/exception/TweetNotFoundException.class org/springdoc/demo/app3/model/Tweet.class org/springdoc/demo/app3/WebfluxDemoApplication.class org/springdoc/demo/app3/repository/TweetRepository.class org/springdoc/demo/app3/payload/ErrorResponse.class org/springdoc/demo/app3/controller/TweetMapper.class org/springdoc/demo/app3/controller/TweetController.class ================================================ FILE: demo-spring-boot-webflux-scalar/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst ================================================ /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/WebfluxDemoApplication.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/controller/ExceptionTranslator.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/controller/TweetController.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/controller/TweetMapper.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/dto/TweetDTO.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/exception/TweetNotFoundException.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/model/Tweet.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/payload/ErrorResponse.java /Users/Badr.NassLahsen/Documents/workspace/main/springdoc-openapi-demos/demo-spring-boot-webflux-scalar/src/main/java/org/springdoc/demo/app3/repository/TweetRepository.java ================================================ FILE: demo-spring-boot-webmvc-scalar/pom.xml ================================================ 4.0.0 org.springdoc springdoc-openapi-demos 3.1.7-SNAPSHOT demo-spring-boot-webmvc-scalar Demo Spring-boot Webmvc Scalar org.springframework.boot spring-boot-starter-web org.springdoc springdoc-openapi-starter-webmvc-scalar org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/java/org/springdoc/demo/services/book/SpringdocApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringdocApplication { public static void main(String[] args) { SpringApplication.run(SpringdocApplication.class, args); } } ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/java/org/springdoc/demo/services/book/controller/BookController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.controller; import java.util.Collection; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springdoc.demo.services.book.exception.BookNotFoundException; import org.springdoc.demo.services.book.model.Book; import org.springdoc.demo.services.book.repository.BookRepository; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; /** * The type Book controller. */ @RestController @RequestMapping("/api/book") public class BookController { /** * The Repository. */ private final BookRepository repository; /** * Instantiates a new Book controller. * * @param repository the repository */ public BookController(BookRepository repository) { this.repository = repository; } /** * Find by id book. * * @param id the id * @return the book */ @GetMapping("/{id}") public Book findById(@PathVariable long id) { return repository.findById(id) .orElseThrow(() -> new BookNotFoundException()); } /** * Find books collection. * * @return the collection */ @GetMapping("/") public Collection findBooks() { return repository.getBooks(); } /** * Update book book. * * @param id the id * @param book the book * @return the book */ @PutMapping("/{id}") @ResponseStatus(HttpStatus.OK) public Book updateBook(@PathVariable("id") final String id, @RequestBody final Book book) { return book; } /** * Patch book book. * * @param id the id * @param book the book * @return the book */ @PatchMapping("/{id}") @ResponseStatus(HttpStatus.OK) public Book patchBook(@PathVariable("id") final String id, @RequestBody final Book book) { return book; } /** * Post book book. * * @param book the book * @return the book */ @PostMapping("/") @ResponseStatus(HttpStatus.CREATED) public Book postBook(@NotNull @Valid @RequestBody final Book book) { return book; } /** * Head book book. * * @return the book */ @RequestMapping(method = RequestMethod.HEAD, value = "/") @ResponseStatus(HttpStatus.OK) public Book headBook() { return new Book(); } /** * Delete book long. * * @param id the id * @return the long */ @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.OK) public long deleteBook(@PathVariable final long id) { return id; } } ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/java/org/springdoc/demo/services/book/exception/BookNotFoundException.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.exception; @SuppressWarnings("serial") public class BookNotFoundException extends RuntimeException { public BookNotFoundException() { } } ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/java/org/springdoc/demo/services/book/exception/GlobalControllerExceptionHandler.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.exception; import java.time.Instant; import org.springframework.core.convert.ConversionFailedException; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @RestControllerAdvice public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(ConversionFailedException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ProblemDetail handleConnversion(RuntimeException e) { ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.getMessage()); problemDetail.setTitle("Bookmark is Not Found"); problemDetail.setProperty("errorCategory", "Generic Exception"); problemDetail.setProperty("timestamp", Instant.now()); return problemDetail; } @ExceptionHandler(BookNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ProblemDetail handleBookNotFound(RuntimeException e) { ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage()); problemDetail.setTitle("Book Not Found"); problemDetail.setProperty("errorCategory", "Generic Exception"); problemDetail.setProperty("timestamp", Instant.now()); return problemDetail; } } ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/java/org/springdoc/demo/services/book/model/Book.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.model; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; /** * The type Book. */ public class Book { /** * The Id. */ private long id; /** * The Title. */ @NotBlank @Size(min = 0, max = 20) private String title; /** * The Author. */ @NotBlank @Size(min = 0, max = 30) private String author; /** * Gets id. * * @return the id */ public long getId() { return id; } /** * Sets id. * * @param id the id */ public void setId(long id) { this.id = id; } /** * Gets title. * * @return the title */ public String getTitle() { return title; } /** * Sets title. * * @param title the title */ public void setTitle(String title) { this.title = title; } /** * Gets author. * * @return the author */ public String getAuthor() { return author; } /** * Sets author. * * @param author the author */ public void setAuthor(String author) { this.author = author; } } ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/java/org/springdoc/demo/services/book/repository/BookRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.book.repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.springdoc.demo.services.book.model.Book; import org.springframework.stereotype.Repository; @Repository public class BookRepository { private Map books = new HashMap<>(); public Optional findById(long id) { return Optional.ofNullable(books.get(id)); } public void add(Book book) { books.put(book.getId(), book); } public Collection getBooks() { return books.values(); } } ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/resources/application.yml ================================================ server: forward-headers-strategy: framework springdoc: version: '@springdoc.version@' ================================================ FILE: demo-spring-boot-webmvc-scalar/src/main/resources/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: demo-spring-cloud-function/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache # Ignore Gradle project-specific cache directory .gradle # Ignore Gradle build output directory build ================================================ FILE: demo-spring-cloud-function/pom.xml ================================================ springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT 4.0.0 demo-spring-cloud-function pom spring-cloud-function-webmvc spring-cloud-function-webflux ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webflux/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache # Ignore Gradle project-specific cache directory .gradle # Ignore Gradle build output directory build ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webflux/pom.xml ================================================ demo-spring-cloud-function org.springdoc 3.1.7-SNAPSHOT 4.0.0 spring-cloud-function-webflux org.springframework.cloud spring-cloud-starter-function-webflux org.springdoc springdoc-openapi-starter-webflux-ui org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webflux/src/main/java/org/springdoc/demo/services/functions/PersonDTO.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.functions; public class PersonDTO { private String email; private String firstName; private String lastName; public PersonDTO() { } public PersonDTO(final String email, final String firstName, final String lastName) { this.email = email; this.firstName = firstName; this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(final String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } } ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webflux/src/main/java/org/springdoc/demo/services/functions/SampleApplication.java ================================================ package org.springdoc.demo.services.functions; import java.util.function.Function; import java.util.function.Supplier; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication() public class SampleApplication { public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components()) .info(new Info().title("spring-cloud-function-webflux OpenAPI Demo").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } @Bean public Function reverseString() { return value -> new StringBuilder(value).reverse().toString(); } @Bean public Function uppercase() { return String::toUpperCase; } @Bean public Function, Flux> lowercase() { return flux -> flux.map(String::toLowerCase); } @Bean public Supplier hello() { return PersonDTO::new; } @Bean public Supplier> words() { return () -> Flux.fromArray(new String[] { "foo", "bar" }); } } ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webflux/src/main/resources/application.yml ================================================ springdoc: version: '@springdoc.version@' swagger-ui: use-root-path: true server: forward-headers-strategy: framework ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webmvc/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache # Ignore Gradle project-specific cache directory .gradle # Ignore Gradle build output directory build ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webmvc/pom.xml ================================================ demo-spring-cloud-function org.springdoc 3.1.7-SNAPSHOT 4.0.0 spring-cloud-function-webmvc org.springframework.cloud spring-cloud-starter-function-web org.springdoc springdoc-openapi-starter-webmvc-ui org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webmvc/src/main/java/org/springdoc/demo/services/functions/PersonDTO.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.functions; public class PersonDTO { private String email; private String firstName; private String lastName; public PersonDTO() { } public PersonDTO(final String email, final String firstName, final String lastName) { this.email = email; this.firstName = firstName; this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(final String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } } ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webmvc/src/main/java/org/springdoc/demo/services/functions/SampleApplication.java ================================================ package org.springdoc.demo.services.functions; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springdoc.core.annotations.RouterOperation; import org.springdoc.core.annotations.RouterOperations; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMethod; @SpringBootApplication() public class SampleApplication { public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components()) .info(new Info().title("spring-cloud-function-webmvc OpenAPI Demo").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } @Bean public Function reverseString() { return value -> new StringBuilder(value).reverse().toString(); } @Bean public Function uppercase() { return String::toUpperCase; } @Bean @RouterOperations({ @RouterOperation(method = RequestMethod.GET, operation = @Operation(description = "Say hello GET", operationId = "lowercaseGET", tags = "positions", responses = @ApiResponse(responseCode = "200", content = @Content(array = @ArraySchema(schema = @Schema(implementation = String.class)))))), @RouterOperation(method = RequestMethod.POST, operation = @Operation(description = "Say hello POST", operationId = "lowercasePOST", tags = "positions", responses = @ApiResponse(responseCode = "200", description = "new desc", content = @Content(array = @ArraySchema(schema = @Schema(implementation = String.class)))))) }) public Function, List> lowercase() { return list -> list.stream().map(String::toLowerCase).collect(Collectors.toList()); } @Bean @RouterOperation(operation = @Operation(description = "Say hello By Id", operationId = "hellome", tags = "persons", responses = @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = PersonDTO.class))))) public Supplier helloSupplier() { return PersonDTO::new; } @Bean public Consumer helloConsumer() { return PersonDTO::getFirstName; } @Bean public Supplier> words() { return () -> Arrays.asList("foo", "bar"); } } ================================================ FILE: demo-spring-cloud-function/spring-cloud-function-webmvc/src/main/resources/application.yml ================================================ springdoc: version: '@springdoc.version@' swagger-ui: use-root-path: true server: forward-headers-strategy: framework ================================================ FILE: demo-spring-data-rest/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-spring-data-rest/pom.xml ================================================ 4.0.0 springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT demo-spring-data-rest org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-rest org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 org.projectlombok lombok org.springdoc springdoc-openapi-starter-webmvc-ui org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-data-rest/src/main/java/org/springdoc/demo/data/rest/Account.java ================================================ /* * * * * * * * * * * Copyright 2019-2020 the original author or authors. * * * * * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * * * you may not use this file except in compliance with the License. * * * * You may obtain a copy of the License at * * * * * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * * * * * Unless required by applicable law or agreed to in writing, software * * * * distributed under the License is distributed on an "AS IS" BASIS, * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * * * See the License for the specific language governing permissions and * * * * limitations under the License. * * * * * * * */ package org.springdoc.demo.data.rest; import java.util.Date; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @Entity public class Account { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @ManyToOne private Customer customer; @Temporal(TemporalType.DATE) private Date expiryDate; public Long getId() { return id; } public Customer getCustomer() { return customer; } public Date getExpiryDate() { return expiryDate; } } ================================================ FILE: demo-spring-data-rest/src/main/java/org/springdoc/demo/data/rest/AccountRepository.java ================================================ /* * * * * * * * * * * Copyright 2019-2020 the original author or authors. * * * * * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * * * you may not use this file except in compliance with the License. * * * * You may obtain a copy of the License at * * * * * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * * * * * Unless required by applicable law or agreed to in writing, software * * * * distributed under the License is distributed on an "AS IS" BASIS, * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * * * See the License for the specific language governing permissions and * * * * limitations under the License. * * * * * * * */ package org.springdoc.demo.data.rest; import java.util.List; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; /** * Repository to manage {@link Account} instances. * */ @RepositoryRestResource @SecurityRequirement(name = "bearer") public interface AccountRepository extends CrudRepository { /** * Returns all accounts belonging to the given {@link Customer}. * * @param customer * @return */ List findByCustomer(@Param("customer") Customer customer); } ================================================ FILE: demo-spring-data-rest/src/main/java/org/springdoc/demo/data/rest/Customer.java ================================================ package org.springdoc.demo.data.rest; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; /** * @author Oliver Gierke */ @Entity public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstname; private String lastname; public Long getId() { return id; } public String getFirstname() { return firstname; } public String getLastname() { return lastname; } } ================================================ FILE: demo-spring-data-rest/src/main/java/org/springdoc/demo/data/rest/CustomerRepository.java ================================================ /* * * * * * * * * * * Copyright 2019-2020 the original author or authors. * * * * * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * * * you may not use this file except in compliance with the License. * * * * You may obtain a copy of the License at * * * * * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * * * * * Unless required by applicable law or agreed to in writing, software * * * * distributed under the License is distributed on an "AS IS" BASIS, * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * * * See the License for the specific language governing permissions and * * * * limitations under the License. * * * * * * * */ package org.springdoc.demo.data.rest; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; /** * Repository to manage {@link Customer} instances. * * @author Oliver Gierke */ @RepositoryRestResource @SecurityRequirement(name = "bearer") public interface CustomerRepository extends CrudRepository, JpaSpecificationExecutor { /** * Returns a page of {@link Customer}s with the given lastname. * * @param lastname * @param pageable * @return */ Page findByLastname(@Param("lastname") String lastname, Pageable pageable); @Override @RestResource(exported = false) void deleteById(Long id); @Override @RestResource(exported = false) void delete(Customer entity); } ================================================ FILE: demo-spring-data-rest/src/main/java/org/springdoc/demo/data/rest/SpringdocApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.data.rest; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringdocApplication { public static void main(String[] args) { SpringApplication.run(SpringdocApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components()) .info(new Info().title("Customer accounts API").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } } ================================================ FILE: demo-spring-data-rest/src/main/resources/application.properties ================================================ springdoc.version=@springdoc.version@ springdoc.swagger-ui.use-root-path=true server.forward-headers-strategy=framework springdoc.cache.disabled=true ================================================ FILE: demo-spring-hateoas/.gitignore ================================================ ###################### # Project Specific ###################### /target/www/** /src/test/javascript/coverage/ ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* /.awcache/* /.cache-loader/* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig classes/ out/ ###################### # Visual Studio Code ###################### .vscode/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log* ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar ###################### # ESLint ###################### .eslintcache ================================================ FILE: demo-spring-hateoas/README.md ================================================ # springdoc-openapi demo with spring-boot-2 hateoas ## Building application ### Pre-requisites - JDK 8+ - maven 3 - docker CLI ### Option 1: Building Executable JAR To create an `executable jar`, simply run: ```sh mvn clean package ``` ### Option 2: Building a non-native OCI Images To create a non-native OCI docker image, simply run: ```sh mvn clean spring-boot:build-image ``` ## Running the native application To run the demo using docker, invoke the following: ```sh docker pull springdocdemos/springdoc-openapi-hateoas-service:latest docker run --rm -p 8080:8080 springdocdemos/springdoc-openapi-hateoas-service:latest ``` ================================================ FILE: demo-spring-hateoas/pom.xml ================================================ 4.0.0 springdoc-openapi-demos org.springdoc 3.1.7-SNAPSHOT demo-spring-hateoas org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-hateoas org.springframework.boot spring-boot-starter-data-jpa com.h2database h2 org.projectlombok lombok org.springdoc springdoc-openapi-starter-webmvc-ui org.springdoc springdoc-openapi-maven-plugin org.springframework.boot spring-boot-maven-plugin ================================================ FILE: demo-spring-hateoas/src/main/java/org/springdoc/demo/services/hateoas/DatabaseLoader.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.hateoas; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; /** * Pre-load some data using a Spring Boot {@link CommandLineRunner}. * * @author Greg Turnquist */ @Component class DatabaseLoader { /** * Use Spring to inject a {@link EmployeeRepository} that can then load data. Since this will run only after the app * is operational, the database will be up. * * @param repository */ @Bean CommandLineRunner init(EmployeeRepository repository) { return args -> { repository.save(new Employee("Frodo", "Baggins", "ring bearer")); repository.save(new Employee("Bilbo", "Baggins", "burglar")); }; } } ================================================ FILE: demo-spring-hateoas/src/main/java/org/springdoc/demo/services/hateoas/Employee.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.hateoas; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * Domain object representing a company employee. Project Lombok keeps actual code at a minimum. {@code @Data} - * Generates getters, setters, toString, hash, and equals functions {@code @Entity} - JPA annotation to flag this class * for DB persistence {@code @NoArgsConstructor} - Create a constructor with no args to support JPA * {@code @AllArgsConstructor} - Create a constructor with all args to support testing * {@code @JsonIgnoreProperties(ignoreUnknow=true)} When converting JSON to Java, ignore any unrecognized attributes. * This is critical for REST because it encourages adding new fields in later versions that won't break. It also allows * things like _links to be ignore as well, meaning HAL documents can be fetched and later posted to the server without * adjustment. * * @author Greg Turnquist */ @Data @Entity @NoArgsConstructor(access = AccessLevel.PRIVATE) @AllArgsConstructor class Employee { @Id @GeneratedValue private Long id; private String firstName; private String lastName; private String role; /** * Useful constructor when id is not yet known. * * @param firstName * @param lastName * @param role */ Employee(String firstName, String lastName, String role) { this.firstName = firstName; this.lastName = lastName; this.role = role; } } ================================================ FILE: demo-spring-hateoas/src/main/java/org/springdoc/demo/services/hateoas/EmployeeController.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.hateoas; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; /** * Spring Web {@link RestController} used to generate a REST API. * * @author Greg Turnquist */ @RestController class EmployeeController { private final EmployeeRepository repository; EmployeeController(EmployeeRepository repository) { this.repository = repository; } /** * Look up all employees, and transform them into a REST collection resource. Then return them through Spring Web's * {@link ResponseEntity} fluent API. */ @GetMapping(path = "/employees", produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity>> findAll() { List> employees = StreamSupport.stream(repository.findAll().spliterator(), false) .map(employee -> EntityModel.of(employee, // linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel(), // linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) // .collect(Collectors.toList()); return ResponseEntity.ok( // CollectionModel.of(employees, // linkTo(methodOn(EmployeeController.class).findAll()).withSelfRel())); } @PostMapping(path = "/employees", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.CREATED) ResponseEntity> newEmployee(@RequestBody Employee employee) { try { Employee savedEmployee = repository.save(employee); EntityModel employeeResource = EntityModel.of(savedEmployee, // linkTo(methodOn(EmployeeController.class).findOne(savedEmployee.getId())).withSelfRel()); return ResponseEntity // .created(new URI(employeeResource.getRequiredLink(IanaLinkRelations.SELF).getHref())) // .body(employeeResource); } catch (URISyntaxException e) { return ResponseEntity.badRequest().body(null); } } /** * Look up a single {@link Employee} and transform it into a REST resource. Then return it through Spring Web's * {@link ResponseEntity} fluent API. * * @param id */ @GetMapping(path = "/employees/{id}", produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity> findOne(@PathVariable long id) { return repository.findById(id) // .map(employee -> EntityModel.of(employee, // linkTo(methodOn(EmployeeController.class).findOne(employee.getId())).withSelfRel(), // linkTo(methodOn(EmployeeController.class).findAll()).withRel("employees"))) // .map(ResponseEntity::ok) // .orElse(ResponseEntity.notFound().build()); } /** * Update existing employee then return a Location header. * * @param employee * @param id * @return */ @PutMapping(path = "/employees/{id}", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.NO_CONTENT) ResponseEntity updateEmployee(@RequestBody Employee employee, @PathVariable long id) throws URISyntaxException { Employee employeeToUpdate = employee; employeeToUpdate.setId(id); repository.save(employeeToUpdate); Link newlyCreatedLink = linkTo(methodOn(EmployeeController.class).findOne(id)).withSelfRel(); return ResponseEntity.noContent().location(new URI(newlyCreatedLink.getHref())).build(); } } ================================================ FILE: demo-spring-hateoas/src/main/java/org/springdoc/demo/services/hateoas/EmployeeRepository.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.hateoas; import org.springframework.data.repository.CrudRepository; /** * A simple Spring Data {@link CrudRepository} for storing {@link Employee}s. * * @author Greg Turnquist */ interface EmployeeRepository extends CrudRepository { } ================================================ FILE: demo-spring-hateoas/src/main/java/org/springdoc/demo/services/hateoas/SpringdocApplication.java ================================================ /* * * * Copyright 2019-2020 the original author or authors. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * https://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package org.springdoc.demo.services.hateoas; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringdocApplication { public static void main(String[] args) { SpringApplication.run(SpringdocApplication.class, args); } @Bean public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) { return new OpenAPI() .components(new Components()) .info(new Info().title("Employee API").version(appVersion) .license(new License().name("Apache 2.0").url("http://springdoc.org"))); } } ================================================ FILE: demo-spring-hateoas/src/main/resources/application.properties ================================================ server.port=8085 spring.hateoas.use-hal-as-default-json-media-type=false springdoc.swagger-ui.operationsSorter=method springdoc.version=@springdoc.version@ springdoc.swagger-ui.use-root-path=true server.forward-headers-strategy=framework ================================================ FILE: pom.xml ================================================ 4.0.0 org.springdoc springdoc-openapi-demos 3.1.7-SNAPSHOT pom Spring-openapi demos Spring-openapi demos https://springdoc.org/ org.springframework.boot spring-boot-starter-parent 3.5.11 The Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt Badr NASS support@springdoc.org BADR https://springdoc.org/ git@github.com:springdoc/springdoc-openapi-demos.git scm:git:git@github.com:springdoc/springdoc-openapi-demos.git scm:git:git@github.com:springdoc/springdoc-openapi-demos.git HEAD Central Portal Snapshots central-portal-snapshots https://central.sonatype.com/repository/maven-snapshots/ false true demo-book-service demo-microservices demo-spring-boot-3-webflux-functional demo-spring-boot-3-webflux demo-spring-boot-3-webmvc demo-oauth2 demo-person-service demo-spring-hateoas demo-spring-data-rest demo-spring-cloud-function demo-spring-boot-webflux-scalar demo-spring-boot-webmvc-scalar 17 @ ${java.version} ${java.version} UTF-8 UTF-8 2.8.17-SNAPSHOT 3.0.1 3.0.1 1.6 2.5.3 3.4.6 1.5 eclipse-temurin:17-jre-ubi9-minimal 2025.0.0 1.4.3 4.18.1 4.18.0 org.springframework.security spring-security-oauth2-authorization-server ${spring-security-oauth2-authorization-server.version} org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springdoc springdoc-openapi-bom ${springdoc.version} pom import org.springframework.boot spring-boot-starter-test test src/main/resources true org.apache.maven.plugins maven-failsafe-plugin 2.22.2 ${project.build.outputDirectory} org.graalvm.buildtools native-maven-plugin org.springframework.boot spring-boot-maven-plugin jib true org.springdoc springdoc-openapi-maven-plugin ${springdoc-openapi-maven-plugin.version} com.google.cloud.tools jib-maven-plugin ${jib-maven-plugin.version} ${jib.from.image} amd64 linux arm64 linux registry.hub.docker.com/springdocdemos/${project.artifactId}:${project.version} -XshowSettings:vm -Dspring.main.lazy-initialization=true -XX:TieredStopAtLevel=1 -XX:+UseSerialGC gpg maven-source-plugin ${maven-source-plugin.version} attach-sources jar maven-javadoc-plugin ${maven-javadoc-plugin.version} all,-missing attach-javadocs jar maven-gpg-plugin ${maven-gpg-plugin.version} sign-artifacts verify sign maven-release-plugin ${maven-release-plugin.version} v@{project.version} true false gpg deploy ================================================ FILE: settings.xml ================================================ ossrh ${env.OSSRH_USER} ${env.OSSRH_PASS}