├── .gitignore ├── LICENSE ├── README.md ├── admin ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── admin │ │ │ └── AdminApplication.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── admin │ └── AdminApplicationTests.java ├── apigw ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── apigw │ │ │ └── ApigwApplication.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── apigw │ └── ApigwApplicationTests.java ├── build.gradle ├── discovery ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── discovery │ │ │ └── DiscoveryApplication.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── discovery │ └── DiscoveryApplicationTests.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── inventory ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── inventory │ │ │ ├── InventoryApplication.java │ │ │ ├── config │ │ │ └── MyBatisConfig.java │ │ │ ├── controller │ │ │ └── InventoryController.java │ │ │ ├── domain │ │ │ ├── Category.java │ │ │ ├── Item.java │ │ │ └── ReduceInfo.java │ │ │ ├── exception │ │ │ ├── IllegalRequestBodyException.java │ │ │ ├── InventoryItemUpdateException.java │ │ │ ├── InventoryLackingException.java │ │ │ ├── InventoryOptimisticException.java │ │ │ └── ItemNotFoundException.java │ │ │ ├── handler │ │ │ ├── ErrorInformation.java │ │ │ └── GlobalExceptionHandler.java │ │ │ ├── repository │ │ │ └── InventoryMapper.java │ │ │ └── service │ │ │ └── InventoryService.java │ └── resources │ │ ├── application.yml │ │ ├── com │ │ └── inventory │ │ │ └── repository │ │ │ └── InventoryMapper.xml │ │ ├── data.sql │ │ ├── drop.sql │ │ ├── pict │ │ ├── apple.jpeg │ │ ├── es2015.jpeg │ │ ├── grape.png │ │ ├── java.png │ │ ├── matrix.jpeg │ │ ├── orange.jpeg │ │ ├── roman.jpeg │ │ ├── star.png │ │ └── titanic.jpeg │ │ └── schema.sql │ └── test │ └── java │ └── com │ └── inventory │ ├── config │ └── TestDataSource.java │ ├── controller │ └── InventoryControllerTest.java │ ├── integration │ └── InventoryApplicationTests.java │ ├── repository │ └── InventoryMapperTest.java │ └── service │ └── InventoryServiceTest.java ├── screenshots ├── bootadmin.png └── microservicedashboard.png ├── settings.gradle ├── shipping ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── shipping │ │ │ ├── ShippingApplication.java │ │ │ ├── config │ │ │ ├── MyBatisConfig.java │ │ │ └── RestTemplateConfig.java │ │ │ ├── controller │ │ │ └── ShippingController.java │ │ │ ├── domain │ │ │ ├── Address.java │ │ │ ├── OrderInfo.java │ │ │ └── OrderedItem.java │ │ │ ├── exception │ │ │ ├── GenerateIdException.java │ │ │ ├── ItemNotFoundException.java │ │ │ ├── ItemUnitLackingException.java │ │ │ ├── NetworkException.java │ │ │ └── ReduceFailedException.java │ │ │ ├── generator │ │ │ ├── Generator.java │ │ │ └── RegisterIdGenerator.java │ │ │ ├── handler │ │ │ ├── ErrorInformation.java │ │ │ └── GlobalExceptionHandler.java │ │ │ ├── repository │ │ │ ├── AddressMapper.java │ │ │ ├── OrderHistoryMapper.java │ │ │ └── OrderedItemMapper.java │ │ │ └── service │ │ │ └── ShippingService.java │ └── resources │ │ ├── application.yml │ │ ├── com │ │ └── shipping │ │ │ └── repository │ │ │ ├── AddressMapper.xml │ │ │ ├── OrderHistoryMapper.xml │ │ │ └── OrderedItemMapper.xml │ │ ├── data.sql │ │ ├── drop.sql │ │ └── schema.sql │ └── test │ └── java │ └── com │ └── shipping │ ├── config │ └── TestDataSource.java │ ├── controller │ └── ShippingControllerTest.java │ ├── generator │ └── RegisterIdGeneratorTest.java │ ├── integration │ └── ShippingApplicationTests.java │ ├── json │ └── TestData.java │ ├── repository │ ├── AddressMapperTest.java │ ├── OrderHistoryMapperTest.java │ └── OrderedItemMapperTest.java │ └── service │ └── ShippingServiceTest.java ├── storefrontend ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── storefrontend │ │ │ ├── StorefrontendApplication.java │ │ │ └── controller │ │ │ └── StoreFrontEndController.java │ └── resources │ │ ├── application.yml │ │ ├── static │ │ ├── asset-manifest.json │ │ ├── bootstrap │ │ │ ├── css │ │ │ │ ├── bootstrap-theme.css │ │ │ │ ├── bootstrap-theme.css.map │ │ │ │ ├── bootstrap-theme.min.css │ │ │ │ ├── bootstrap-theme.min.css.map │ │ │ │ ├── bootstrap.css │ │ │ │ ├── bootstrap.css.map │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ └── js │ │ │ │ ├── bootstrap.js │ │ │ │ ├── bootstrap.min.js │ │ │ │ └── npm.js │ │ ├── css │ │ │ ├── main.ce273286.css │ │ │ └── main.ce273286.css.map │ │ ├── favicon.ico │ │ ├── font-awesome-4.7.0 │ │ │ ├── HELP-US-OUT.txt │ │ │ ├── css │ │ │ │ ├── font-awesome.css │ │ │ │ └── font-awesome.min.css │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── less │ │ │ │ ├── animated.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── core.less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── icons.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── mixins.less │ │ │ │ ├── path.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── screen-reader.less │ │ │ │ ├── stacked.less │ │ │ │ └── variables.less │ │ │ └── scss │ │ │ │ ├── _animated.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _screen-reader.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _variables.scss │ │ │ │ └── font-awesome.scss │ │ ├── js │ │ │ ├── main.301a64e3.js │ │ │ └── main.301a64e3.js.map │ │ ├── manifest.json │ │ └── service-worker.js │ │ └── templates │ │ └── index.html │ └── test │ └── java │ └── com │ └── storefrontend │ └── StorefrontendApplicationTests.java └── storefrontend_proto ├── .eslintrc.js ├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json └── src ├── App.css ├── App.js ├── App.test.js ├── components ├── cart │ ├── Cart.js │ └── CartHolder.js ├── footer │ ├── Footer.css │ └── Footer.js ├── header │ └── Header.js └── item │ ├── Item.css │ ├── Item.js │ └── ItemList.js ├── index.css ├── index.js ├── logo.svg └── registerServiceWorker.js /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.gradle 3 | /logfile 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-microservice-for-e-commerce 2 | The example application is an e-commerce application which is based on Micro Service and Spring Cloud. 3 | 4 | ## Reference 5 | http://microservices.io/patterns/microservices.html 6 | 7 | ## About the example application 8 | This example application provides REST API for viewing and getting inventory item and saving shipping information. 9 | 10 | ## Stacks 11 | - Spring Boot 12 | - MyBatis 13 | - Spring Cloud (Eureka, Ribon, and so on) 14 | - Thymeleaf 15 | - ... 16 | 17 | ## REST API specification of spring-microservice-for-e-commerce 18 | 19 | ### inventory application 20 | 21 | - GET /inventory/items/ 22 | + Gets all items. 23 | - GET /inventory/items/category/{id} 24 | + Gets items which belong to specify category. 25 | - GET /inventory/items/{id} 26 | + Gets specify item. 27 | - POST /inventory/update/ 28 | + When user orders some item, this bulk API is called from shipping service. 29 | + NOTE: This API needs request body of list which includes Item id, Item unit and Item versionNo. 30 | - GET /inventory/check/{id} 31 | + This is internal API that returns Item information, which only includes Item id, Item unit and Item versionNo. 32 | + e.g: /inventory/check/1/ 33 | 34 | If you specified invalid path (e.g. /inventory/iiiite, /inventory/items, /inventory/item/aaaa/) on browser or REST access, 35 | this example application will be returned JSON message which includes an error infomation. 36 | 37 | ### shipping application. 38 | 39 | - /shipping/order/ 40 | + Order item with JSON message as follows. 41 | 42 | ```json 43 | { 44 | "item":[ 45 | { 46 | "id":1, 47 | "unit":10 48 | } 49 | ], 50 | "senderName":"testSender", 51 | "receiverName":"testReceiver", 52 | "address":{ 53 | "zipCode":"123-4567", 54 | "location":"locloclocloc" 55 | } 56 | } 57 | ``` 58 | 59 | ### storefrontend application 60 | 61 | TODO 62 | 63 | ### apigw application 64 | 65 | TODO 66 | 67 | ### discovery application 68 | 69 | TODO 70 | 71 | ### admin application 72 | 73 | TODO 74 | 75 | ## How to run 76 | 77 | ### Run using Gradle command. 78 | Run all application using Gradle command on spring-microservice-for-e-commerce directory. 79 | ```gradle 80 | $spring-microservice-for-e-commerce> ./gradlew bootRun --parallel --max-workers=6 81 | ``` 82 | 83 | When you run Gradle command, there is a point to be mindful of. 84 | First, your pc must be online. 85 | If your pc is offline, Netflix products won't work. 86 | 87 | ### Spring Boot Admin 88 | 89 | #### Microservice Dashboard 90 | 91 | Please access 'http://localhost:8110'. 92 | 93 | ![Microservice Dashboard](./screenshots/microservicedashboard.png) 94 | 95 | #### Spring Boot Admin 96 | 97 | Please access 'http://localhost:8110/admin/' 98 | 99 | ![Spring Boot Admin](./screenshots/bootadmin.png) 100 | 101 | ### Microservices Dashboard 102 | 103 | 104 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /admin/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.4.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | group = 'com.example' 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | ext { 27 | springCloudVersion = 'Camden.SR5' 28 | } 29 | 30 | dependencies { 31 | compile('org.springframework.boot:spring-boot-starter-actuator') 32 | compile('org.springframework.cloud:spring-cloud-starter-eureka') 33 | testCompile('org.springframework.boot:spring-boot-starter-test') 34 | compile('de.codecentric:spring-boot-admin-server:1.4.6') 35 | compile('de.codecentric:spring-boot-admin-server-ui:1.4.6') 36 | compile('be.ordina:microservices-dashboard-server:1.0.1') 37 | } 38 | 39 | dependencyManagement { 40 | imports { 41 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /admin/src/main/java/com/admin/AdminApplication.java: -------------------------------------------------------------------------------- 1 | package com.admin; 2 | 3 | import be.ordina.msdashboard.EnableMicroservicesDashboardServer; 4 | import de.codecentric.boot.admin.config.EnableAdminServer; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | 9 | @EnableDiscoveryClient 10 | @EnableAdminServer 11 | @EnableMicroservicesDashboardServer 12 | @SpringBootApplication 13 | public class AdminApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(AdminApplication.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /admin/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8110 3 | 4 | info: 5 | name: admin 6 | 7 | spring: 8 | application: 9 | name: admin 10 | output: 11 | ansi: 12 | enabled: ALWAYS 13 | boot: 14 | admin: 15 | context-path: /admin 16 | 17 | eureka: 18 | client: 19 | serviceUrl: 20 | defaultZone: http://localhost:8100/eureka/ 21 | 22 | logging: 23 | file: logfile/admin.log 24 | -------------------------------------------------------------------------------- /admin/src/test/java/com/admin/AdminApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.admin; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class AdminApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /apigw/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /apigw/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.4.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | group = 'com.example' 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | ext { 27 | springCloudVersion = 'Camden.SR5' 28 | } 29 | 30 | dependencies { 31 | compile('org.springframework.boot:spring-boot-starter-actuator') 32 | compile('org.springframework.cloud:spring-cloud-starter-eureka') 33 | compile('org.springframework.cloud:spring-cloud-starter-ribbon') 34 | compile('org.springframework.cloud:spring-cloud-starter-zuul') 35 | testCompile('org.springframework.boot:spring-boot-starter-test') 36 | } 37 | 38 | dependencyManagement { 39 | imports { 40 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apigw/src/main/java/com/apigw/ApigwApplication.java: -------------------------------------------------------------------------------- 1 | package com.apigw; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 7 | 8 | @EnableZuulProxy 9 | @EnableDiscoveryClient 10 | @SpringBootApplication 11 | public class ApigwApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ApigwApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apigw/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8000 3 | 4 | spring: 5 | application: 6 | name: apigw 7 | output: 8 | ansi: 9 | enabled: ALWAYS 10 | 11 | eureka: 12 | client: 13 | serviceUrl: 14 | defaultZone: http://localhost:8100/eureka/ 15 | 16 | info: 17 | name: apigw 18 | 19 | logging: 20 | file: logfile/apigw.log 21 | 22 | 23 | zuul: 24 | routes: 25 | front: 26 | path: /storefrontend/** 27 | serviceId: storefrontend 28 | inventory: 29 | path: /shop/** 30 | serviceId: inventory 31 | shipping: 32 | path: /shipping/** 33 | serviceId: shipping 34 | -------------------------------------------------------------------------------- /apigw/src/test/java/com/apigw/ApigwApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.apigw; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class ApigwApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/build.gradle -------------------------------------------------------------------------------- /discovery/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /discovery/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.4.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | group = 'com.example' 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | ext { 27 | springCloudVersion = 'Camden.SR5' 28 | } 29 | 30 | dependencies { 31 | compile('org.springframework.boot:spring-boot-starter-actuator') 32 | compile('org.springframework.cloud:spring-cloud-starter-eureka-server') 33 | testCompile('org.springframework.boot:spring-boot-starter-test') 34 | } 35 | 36 | dependencyManagement { 37 | imports { 38 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /discovery/src/main/java/com/discovery/DiscoveryApplication.java: -------------------------------------------------------------------------------- 1 | package com.discovery; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @EnableEurekaServer 8 | @SpringBootApplication 9 | public class DiscoveryApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(DiscoveryApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /discovery/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8100 3 | 4 | info: 5 | name: ServiceDiscovery 6 | 7 | spring: 8 | application: 9 | name: ServiceDiscovery 10 | output: 11 | ansi: 12 | enabled: ALWAYS 13 | 14 | eureka: 15 | client: 16 | serviceUrl: 17 | defaultZone: http://localhost:8100/eureka/ 18 | 19 | logging: 20 | file: logfile/discovery.log 21 | -------------------------------------------------------------------------------- /discovery/src/test/java/com/discovery/DiscoveryApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.discovery; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DiscoveryApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Nov 28 23:59:17 JST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /inventory/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /inventory/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.4.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | group = 'com.example' 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | ext { 27 | springCloudVersion = 'Camden.SR5' 28 | } 29 | 30 | dependencies { 31 | compile('org.springframework.boot:spring-boot-starter-actuator') 32 | compile('org.springframework.cloud:spring-cloud-starter-eureka') 33 | compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1') 34 | runtime('com.h2database:h2') 35 | runtime('org.springframework.boot:spring-boot-devtools') 36 | testCompile('org.springframework.boot:spring-boot-starter-test') 37 | compile('org.jolokia:jolokia-core') 38 | compileOnly('org.projectlombok:lombok') 39 | } 40 | 41 | dependencyManagement { 42 | imports { 43 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/InventoryApplication.java: -------------------------------------------------------------------------------- 1 | package com.inventory; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.context.ConfigurableApplicationContext; 7 | import org.springframework.web.servlet.DispatcherServlet; 8 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 | 10 | @EnableDiscoveryClient 11 | @SpringBootApplication 12 | @EnableWebMvc 13 | public class InventoryApplication { 14 | 15 | public static void main(String[] args) { 16 | final ConfigurableApplicationContext context = SpringApplication.run(InventoryApplication.class, args); 17 | DispatcherServlet dispatcherServlet = ((DispatcherServlet) context.getBean("dispatcherServlet")); 18 | dispatcherServlet.setThrowExceptionIfNoHandlerFound(true); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/config/MyBatisConfig.java: -------------------------------------------------------------------------------- 1 | package com.inventory.config; 2 | 3 | import org.mybatis.spring.SqlSessionFactoryBean; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Lazy; 9 | 10 | import javax.sql.DataSource; 11 | 12 | @Configuration 13 | @MapperScan("com.inventory.repository") 14 | public class MyBatisConfig { 15 | 16 | private final DataSource dataSource; 17 | 18 | @Autowired 19 | @Lazy // FIXME: Workaround : The dependencies of some of the beans in the application context form a cycle: 20 | public MyBatisConfig(final DataSource dataSource) { 21 | this.dataSource = dataSource; 22 | } 23 | 24 | @Bean 25 | public SqlSessionFactoryBean sqlSessionFactoryBean() { 26 | final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 27 | sqlSessionFactoryBean.setDataSource(dataSource); 28 | return sqlSessionFactoryBean; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/controller/InventoryController.java: -------------------------------------------------------------------------------- 1 | package com.inventory.controller; 2 | 3 | import com.inventory.domain.Item; 4 | import com.inventory.exception.IllegalRequestBodyException; 5 | import com.inventory.service.InventoryService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.List; 11 | 12 | @CrossOrigin // To allow accessing from other domain. 13 | @RequestMapping("/inventory") 14 | @RestController 15 | @Validated 16 | public class InventoryController { 17 | 18 | private final InventoryService inventoryService; 19 | 20 | @Autowired 21 | public InventoryController(final InventoryService inventoryService) { 22 | this.inventoryService = inventoryService; 23 | } 24 | 25 | @GetMapping("/items/") 26 | public List items() { 27 | return inventoryService.items(); 28 | } 29 | 30 | @GetMapping("/items/category/{id}/") 31 | public List itemsOfCategory(@PathVariable("id") final Integer categoryId) { 32 | return inventoryService.items(categoryId); 33 | } 34 | 35 | @GetMapping("/item/{id}/") 36 | public Item item(@PathVariable("id") final Integer id) { 37 | return inventoryService.item(id); 38 | } 39 | 40 | @PostMapping("/update/") 41 | public String reduce(@RequestBody final List items) { 42 | return inventoryService.reduce(items); 43 | } 44 | 45 | @GetMapping("/check/{id}/") 46 | public Item check(@PathVariable final String id) { 47 | try { 48 | return inventoryService.check(Integer.parseInt(id)); 49 | } catch (NumberFormatException e) { 50 | throw new IllegalRequestBodyException(e); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/domain/Category.java: -------------------------------------------------------------------------------- 1 | package com.inventory.domain; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @ToString 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class Category { 11 | 12 | private Integer id; 13 | 14 | private String name; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/domain/Item.java: -------------------------------------------------------------------------------- 1 | package com.inventory.domain; 2 | 3 | import lombok.*; 4 | 5 | 6 | @Setter 7 | @Getter 8 | @ToString 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class Item { 12 | 13 | private Integer id; 14 | 15 | private String name; 16 | 17 | private Integer price; 18 | 19 | private Integer unit; 20 | 21 | private String description; 22 | 23 | // FIXME: I don't know how to handle a big data likes image data. 24 | // private InputStream pict; 25 | private String pict; 26 | 27 | private Category category; 28 | 29 | private Long versionno; 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | // FIXME This code is ad-hoc code to pass the InventoryController#testReduce_validValue() 34 | // because the instance of Item that I use with when() is different than the one used in InventoryService#update(). 35 | // Unless I override equals(), the Mockito won't return expected value that I defined with when(). 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/domain/ReduceInfo.java: -------------------------------------------------------------------------------- 1 | package com.inventory.domain; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @ToString 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class ReduceInfo { 11 | 12 | private Integer id; 13 | 14 | private Integer unit; 15 | 16 | private Long versionno; 17 | 18 | @Override 19 | public boolean equals(Object obj) { 20 | // FIXME This code is ad-hoc code to pass the InventoryController#testReduce_validValue() 21 | // because the instance of ReduceInfo that I use with when() is different than the one used in InventoryService#update(). 22 | // Unless I override equals(), the Mockito won't return expected value that I defined with when(). 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/exception/IllegalRequestBodyException.java: -------------------------------------------------------------------------------- 1 | package com.inventory.exception; 2 | 3 | public class IllegalRequestBodyException extends RuntimeException { 4 | public IllegalRequestBodyException() { 5 | super("the data of request is not valid."); 6 | } 7 | 8 | public IllegalRequestBodyException(Throwable e) { 9 | super("the data of request is not valid.", e); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/exception/InventoryItemUpdateException.java: -------------------------------------------------------------------------------- 1 | package com.inventory.exception; 2 | 3 | public class InventoryItemUpdateException extends RuntimeException { 4 | public InventoryItemUpdateException() { 5 | super("an error occurs during updating item."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/exception/InventoryLackingException.java: -------------------------------------------------------------------------------- 1 | package com.inventory.exception; 2 | 3 | public class InventoryLackingException extends RuntimeException { 4 | public InventoryLackingException() { 5 | super("there is not enough amount."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/exception/InventoryOptimisticException.java: -------------------------------------------------------------------------------- 1 | package com.inventory.exception; 2 | 3 | public class InventoryOptimisticException extends RuntimeException { 4 | 5 | public InventoryOptimisticException() { 6 | super("The version no of specified Item was updated by other user."); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/exception/ItemNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.inventory.exception; 2 | 3 | public class ItemNotFoundException extends RuntimeException { 4 | public ItemNotFoundException() { 5 | super(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/handler/ErrorInformation.java: -------------------------------------------------------------------------------- 1 | package com.inventory.handler; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ErrorInformation { 7 | 8 | private final String errorId; 9 | private final String errorMessage; 10 | private final String solution; 11 | 12 | private ErrorInformation(final Builder builder) { 13 | errorId = builder.errorId; 14 | errorMessage = builder.errorMessage; 15 | solution = builder.solution; 16 | } 17 | 18 | public static class Builder { 19 | 20 | private final String errorId; 21 | private final String errorMessage; 22 | private String solution; 23 | 24 | public Builder(final String errorId, final String errorMessage) { 25 | this.errorId = errorId; 26 | this.errorMessage = errorMessage; 27 | } 28 | 29 | public Builder countermeasure(final String solution) { 30 | this.solution = solution; 31 | return this; 32 | } 33 | 34 | public ErrorInformation build() { 35 | return new ErrorInformation(this); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.inventory.handler; 2 | 3 | import com.inventory.exception.IllegalRequestBodyException; 4 | import com.inventory.exception.ItemNotFoundException; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.ResponseStatus; 9 | import org.springframework.web.bind.annotation.RestControllerAdvice; 10 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; 11 | import org.springframework.web.servlet.NoHandlerFoundException; 12 | 13 | @RestControllerAdvice 14 | @Component 15 | public class GlobalExceptionHandler { 16 | 17 | @ExceptionHandler 18 | @ResponseStatus(HttpStatus.NOT_FOUND) 19 | public ErrorInformation handle(final NoHandlerFoundException e) { 20 | return new ErrorInformation 21 | .Builder("SVR_URI_002", "an invalid url was specified for path of URI.").build(); 22 | } 23 | 24 | @ExceptionHandler 25 | @ResponseStatus(HttpStatus.BAD_REQUEST) 26 | public ErrorInformation handle(final MethodArgumentTypeMismatchException e) { 27 | return new ErrorInformation 28 | .Builder("SVR_URI_001", "an invalid parameter was specified for path of URI.").build(); 29 | } 30 | 31 | @ExceptionHandler 32 | @ResponseStatus(HttpStatus.OK) 33 | public ErrorInformation handle(final ItemNotFoundException e) { 34 | return new ErrorInformation.Builder("SVR_URI_010", "could not find specified item in the inventory service.").build(); 35 | } 36 | 37 | @ExceptionHandler 38 | @ResponseStatus(HttpStatus.BAD_REQUEST) 39 | public ErrorInformation handle(final IllegalRequestBodyException e) { 40 | return new ErrorInformation.Builder("SVR_REQUEST_001", e.getMessage()).build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/repository/InventoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.inventory.repository; 2 | 3 | import com.inventory.domain.Item; 4 | import com.inventory.domain.ReduceInfo; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | @Mapper 11 | public interface InventoryMapper { 12 | 13 | List selectItemsByCategoryId(@Param("categoryId") final Integer categoryId); 14 | 15 | Item selectItemById(@Param("id") final Integer id); 16 | 17 | boolean update(final ReduceInfo reduceInfo); 18 | } 19 | -------------------------------------------------------------------------------- /inventory/src/main/java/com/inventory/service/InventoryService.java: -------------------------------------------------------------------------------- 1 | package com.inventory.service; 2 | 3 | import com.inventory.domain.Item; 4 | import com.inventory.domain.ReduceInfo; 5 | import com.inventory.exception.InventoryItemUpdateException; 6 | import com.inventory.exception.InventoryLackingException; 7 | import com.inventory.exception.InventoryOptimisticException; 8 | import com.inventory.exception.ItemNotFoundException; 9 | import com.inventory.repository.InventoryMapper; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.List; 15 | 16 | @Service 17 | public class InventoryService { 18 | 19 | private final InventoryMapper inventoryMapper; 20 | private static final Integer NO_CATEGORY_KEY = null; 21 | 22 | @Autowired 23 | public InventoryService(final InventoryMapper inventoryMapper) { 24 | this.inventoryMapper = inventoryMapper; 25 | } 26 | 27 | public List items() { 28 | return inventoryMapper.selectItemsByCategoryId(NO_CATEGORY_KEY); 29 | } 30 | 31 | public Item item(final Integer id) { 32 | final Item item = inventoryMapper.selectItemById(id); 33 | if (item == null) { 34 | throw new ItemNotFoundException(); 35 | } 36 | return item; 37 | } 38 | 39 | public List items(final Integer categoryId) { 40 | return inventoryMapper.selectItemsByCategoryId(categoryId); 41 | } 42 | 43 | @Transactional 44 | public String reduce(final List items) { 45 | 46 | items.forEach(item -> { 47 | final Item selectedItem = inventoryMapper.selectItemById(item.getId()); 48 | if (item.getVersionno() != selectedItem.getVersionno()) { 49 | throw new InventoryOptimisticException(); 50 | } 51 | final Integer originalUnit = selectedItem.getUnit(); 52 | final Integer calcUnit = originalUnit - item.getUnit(); 53 | if (calcUnit < 0) { 54 | throw new InventoryLackingException(); 55 | } 56 | final ReduceInfo reduceInfo = new ReduceInfo(); 57 | reduceInfo.setId(item.getId()); 58 | reduceInfo.setVersionno(item.getVersionno()); 59 | reduceInfo.setUnit(calcUnit); 60 | final boolean result = inventoryMapper.update(reduceInfo); 61 | if (!result) { 62 | throw new InventoryItemUpdateException(); 63 | } 64 | }); 65 | return "success"; 66 | } 67 | 68 | public Item check(final Integer id) { 69 | final Item item = inventoryMapper.selectItemById(id); 70 | if (item == null) { 71 | throw new ItemNotFoundException(); 72 | } 73 | return wdightSaving(item); 74 | } 75 | 76 | private Item wdightSaving(Item item) { 77 | item.setCategory(null); 78 | item.setPict(null); 79 | item.setDescription(null); 80 | return item; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /inventory/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8200 3 | 4 | spring: 5 | application: 6 | name: inventory 7 | output: 8 | ansi: 9 | enabled: ALWAYS 10 | 11 | eureka: 12 | client: 13 | serviceUrl: 14 | defaultZone: http://localhost:8100/eureka/ 15 | 16 | info: 17 | name: inventory 18 | 19 | logging: 20 | file: logfile/inventory.log 21 | -------------------------------------------------------------------------------- /inventory/src/main/resources/com/inventory/repository/InventoryMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 41 | 42 | 62 | 63 | 64 | UPDATE ITEM 65 | SET UNIT = #{unit}, 66 | VERSIONNO = #{versionno} + 1 67 | WHERE ID = #{id} AND VERSIONNO = #{versionno} 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /inventory/src/main/resources/drop.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ITEM; 2 | DROP TABLE IF EXISTS CATEGORY; 3 | -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/apple.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/apple.jpeg -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/es2015.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/es2015.jpeg -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/grape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/grape.png -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/java.png -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/matrix.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/matrix.jpeg -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/orange.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/orange.jpeg -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/roman.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/roman.jpeg -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/star.png -------------------------------------------------------------------------------- /inventory/src/main/resources/pict/titanic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/inventory/src/main/resources/pict/titanic.jpeg -------------------------------------------------------------------------------- /inventory/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS CATEGORY ( 2 | ID INT(100) PRIMARY KEY, 3 | NAME VARCHAR(20) 4 | ); 5 | 6 | CREATE TABLE ITEM ( 7 | ID INT(10000) DEFAULT 1 NOT NULL AUTO_INCREMENT, 8 | NAME VARCHAR(100) NOT NULL, 9 | PRICE INT(100000) NOT NULL, 10 | UNIT INT(100) NOT NULL, 11 | DESCRIPTION VARCHAR(200) NOT NULL, 12 | PICT CLOB, 13 | CATEGORY_ID INT(100), 14 | VERSIONNO BIGINT NOT NULL, 15 | FOREIGN KEY (CATEGORY_ID) REFERENCES CATEGORY(ID) 16 | ); 17 | -------------------------------------------------------------------------------- /inventory/src/test/java/com/inventory/config/TestDataSource.java: -------------------------------------------------------------------------------- 1 | package com.inventory.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 7 | 8 | import javax.sql.DataSource; 9 | 10 | @Configuration 11 | public class TestDataSource { 12 | 13 | @Bean 14 | public DataSource dataSource() { 15 | return new EmbeddedDatabaseBuilder() 16 | .setType(EmbeddedDatabaseType.H2) 17 | .build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /inventory/src/test/java/com/inventory/controller/InventoryControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.inventory.controller; 2 | 3 | import com.inventory.domain.Category; 4 | import com.inventory.domain.Item; 5 | import com.inventory.exception.ItemNotFoundException; 6 | import com.inventory.service.InventoryService; 7 | import org.junit.Before; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.ExpectedException; 11 | import org.junit.runner.RunWith; 12 | import org.mockito.InjectMocks; 13 | import org.mockito.Mock; 14 | import org.mockito.runners.MockitoJUnitRunner; 15 | import org.springframework.boot.test.context.SpringBootTest; 16 | 17 | import java.util.Arrays; 18 | 19 | import static org.hamcrest.Matchers.is; 20 | import static org.hamcrest.Matchers.nullValue; 21 | import static org.hamcrest.Matchers.samePropertyValuesAs; 22 | import static org.junit.Assert.*; 23 | import static org.mockito.Mockito.when; 24 | 25 | @SpringBootTest 26 | @RunWith(MockitoJUnitRunner.class) 27 | public class InventoryControllerTest { 28 | 29 | @InjectMocks 30 | InventoryController sut; 31 | 32 | @Mock 33 | InventoryService inventoryService; 34 | 35 | @Rule 36 | public ExpectedException expectedException = ExpectedException.none(); 37 | 38 | @Before 39 | public void setUp() throws Exception { 40 | when(inventoryService.reduce(Arrays.asList(new Item(1,"test", 10000, 2, "desc", null, null, 0L)))).thenReturn("success"); 41 | when(inventoryService.item(null)).thenReturn(null); 42 | when(inventoryService.item(1)).thenReturn(new Item(1, "Foo", 10000, 10, "Desc of Foo", null, new Category(1,"Bar"), 0L)); 43 | when(inventoryService.items()).thenReturn(Arrays.asList(new Item(), new Item(), new Item())); 44 | when(inventoryService.items(2)).thenReturn(Arrays.asList(new Item(), new Item())); 45 | when(inventoryService.check(1)).thenReturn(new Item(1, "Foo", 10000, 10, null, null, null, 0L)); 46 | when(inventoryService.check(999)).thenThrow(new ItemNotFoundException()); 47 | } 48 | 49 | @Test 50 | public void testItems() throws Exception { 51 | assertThat(sut.items().size(), is(3)); 52 | } 53 | 54 | @Test 55 | public void testItemsOfCategory_validValue() throws Exception { 56 | assertThat(sut.itemsOfCategory(2).size(), is(2)); 57 | } 58 | 59 | @Test 60 | public void testItemsOfCategory_invalidValue() throws Exception { 61 | assertThat(sut.itemsOfCategory(Integer.MAX_VALUE).size(), is(0)); 62 | } 63 | 64 | @Test 65 | public void testItem_validValue() throws Exception { 66 | final Item item = sut.item(1); 67 | assertThat(item.getId(), is(1)); 68 | assertThat(item.getName(), is("Foo")); 69 | assertThat(item.getCategory(), samePropertyValuesAs(new Category(1, "Bar"))); 70 | } 71 | 72 | @Test 73 | public void testItem_invalidValue() throws Exception { 74 | assertThat(sut.item(9), is(nullValue())); 75 | } 76 | 77 | @Test 78 | public void testReduce_validValue() throws Exception { 79 | assertThat(sut.reduce( 80 | Arrays.asList( 81 | new Item(1,"test", 10000, 2, "desc", null, null, 0L) 82 | )), 83 | is("success")); 84 | } 85 | 86 | @Test 87 | public void testCheck_validValue() throws Exception { 88 | final Item item = sut.check("1"); 89 | assertThat(item.getId(), is(1)); 90 | assertThat(item.getName(), is("Foo")); 91 | assertThat(item.getCategory(), nullValue()); 92 | assertThat(item.getDescription(), nullValue()); 93 | assertThat(item.getPict(), nullValue()); 94 | } 95 | 96 | @Test 97 | public void testCheck_invalidValue() throws Exception { 98 | expectedException.expect(ItemNotFoundException.class); 99 | sut.check("999"); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /inventory/src/test/java/com/inventory/integration/InventoryApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.inventory.integration; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.inventory.InventoryApplication; 5 | import com.inventory.config.MyBatisConfig; 6 | import com.inventory.config.TestDataSource; 7 | import com.inventory.domain.Item; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.http.MediaType; 14 | import org.springframework.test.context.ContextConfiguration; 15 | import org.springframework.test.context.jdbc.Sql; 16 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 17 | import org.springframework.test.context.web.WebAppConfiguration; 18 | import org.springframework.test.web.servlet.MockMvc; 19 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 20 | import org.springframework.web.context.WebApplicationContext; 21 | 22 | import java.util.Arrays; 23 | 24 | import static org.hamcrest.Matchers.is; 25 | import static org.hamcrest.Matchers.nullValue; 26 | import static org.hamcrest.collection.IsCollectionWithSize.hasSize; 27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 28 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 29 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 30 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 31 | 32 | @RunWith(SpringJUnit4ClassRunner.class) 33 | @SpringBootTest(classes = {TestDataSource.class, MyBatisConfig.class}) 34 | @WebAppConfiguration 35 | @ContextConfiguration(classes = InventoryApplication.class) 36 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 37 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 38 | public class InventoryApplicationTests { 39 | 40 | MockMvc mockMvc; 41 | 42 | @Autowired 43 | WebApplicationContext webApplicationContext; 44 | 45 | @Before 46 | public void setUp() throws Exception { 47 | this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); 48 | } 49 | 50 | /** 51 | * This is a test case for path of /inventory/items/. 52 | * 53 | * @throws Exception 54 | */ 55 | @Test 56 | public void testItems() throws Exception { 57 | this.mockMvc.perform(get("/inventory/items/") 58 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 59 | .andExpect(status().isOk()) 60 | .andExpect(content().contentType("application/json;charset=UTF-8")) 61 | .andExpect(jsonPath("$", hasSize(9))) 62 | .andExpect(jsonPath("$[0].id", is(1))) 63 | .andExpect(jsonPath("$[0].name", is("Apple"))) 64 | .andExpect(jsonPath("$[0].category.id", is(1))) 65 | .andExpect(jsonPath("$[0].category.name", is("FOOD"))) 66 | .andExpect(jsonPath("$[7].id", is(8))) 67 | .andExpect(jsonPath("$[7].name", is("Matrix"))) 68 | .andExpect(jsonPath("$[7].category.id", is(3))) 69 | .andExpect(jsonPath("$[7].category.name", is("DVD"))) 70 | .andDo(print()); 71 | } 72 | 73 | /** 74 | * This is a test case for path of /inventory/item/{id}/. 75 | * 76 | * @throws Exception 77 | */ 78 | @Test 79 | public void testItem_validValue() throws Exception { 80 | this.mockMvc.perform(get("/inventory/item/5/") 81 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 82 | .andExpect(status().isOk()) 83 | .andExpect(content().contentType("application/json;charset=UTF-8")) 84 | .andExpect(jsonPath("$.id", is(5))) 85 | .andExpect(jsonPath("$.name", is("ES2015 In Action"))) 86 | .andExpect(jsonPath("$.category.id", is(2))) 87 | .andExpect(jsonPath("$.category.name", is("BOOK"))) 88 | .andDo(print()); 89 | } 90 | 91 | /** 92 | * This is a test case for path of /inventory/item/9999/. 93 | * 94 | * @throws Exception 95 | */ 96 | @Test 97 | public void testItem_invalidValue() throws Exception { 98 | this.mockMvc.perform(get("/inventory/item/9999/") 99 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 100 | .andExpect(status().isOk()) 101 | .andExpect(content().contentType("application/json;charset=UTF-8")) 102 | .andExpect(jsonPath("$.errorId", is("SVR_URI_010"))) 103 | .andExpect(jsonPath("$.errorMessage", is("could not find specified item in the inventory service."))) 104 | .andDo(print()); 105 | } 106 | 107 | 108 | /** 109 | * This is a test case for path of /inventory/items/category/{id}/. 110 | * 111 | * @throws Exception 112 | */ 113 | @Test 114 | public void testItemsOfCategory_validValue() throws Exception { 115 | this.mockMvc.perform(get("/inventory/items/category/2/") 116 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 117 | .andExpect(status().isOk()) 118 | .andExpect(content().contentType("application/json;charset=UTF-8")) 119 | .andExpect(jsonPath("$", hasSize(2))) 120 | .andExpect(jsonPath("$[0].id", is(4))) 121 | .andExpect(jsonPath("$[0].name", is("Java In Action"))) 122 | .andExpect(jsonPath("$[0].category.id", is(2))) 123 | .andExpect(jsonPath("$[0].category.name", is("BOOK"))) 124 | .andExpect(jsonPath("$[1].id", is(5))) 125 | .andExpect(jsonPath("$[1].name", is("ES2015 In Action"))) 126 | .andExpect(jsonPath("$[1].category.id", is(2))) 127 | .andExpect(jsonPath("$[1].category.name", is("BOOK"))) 128 | .andDo(print()); 129 | } 130 | 131 | /** 132 | * This is a test case for path of /inventory/items/category/9999/. 133 | * 134 | * @throws Exception 135 | */ 136 | @Test 137 | public void testItemsOfCategory_invalidValue() throws Exception { 138 | this.mockMvc.perform(get("/inventory/items/category/9999/") 139 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 140 | .andExpect(status().isOk()) 141 | .andExpect(content().contentType("application/json;charset=UTF-8")) 142 | .andExpect(jsonPath("$", hasSize(0))) 143 | .andDo(print()); 144 | } 145 | 146 | /** 147 | * This is a test case for path of /inventory/item/aaa/. 148 | * 149 | * @throws Exception 150 | */ 151 | @Test 152 | public void testItem_invalidValue_notNumber() throws Exception { 153 | this.mockMvc.perform(get("/inventory/item/aaa/") 154 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 155 | .andExpect(status().isBadRequest()) 156 | .andExpect(content().contentType("application/json;charset=UTF-8")) 157 | .andExpect(jsonPath("$.errorId", is("SVR_URI_001"))) 158 | .andExpect(jsonPath("$.errorMessage", is("an invalid parameter was specified for path of URI."))) 159 | .andDo(print()); 160 | } 161 | 162 | // TODO: I'll investigate problem of this test case someday. 163 | // /** 164 | // * This is a test case for path of /inventory/iteeeee. 165 | // * @throws Exception 166 | // */ 167 | // @Test 168 | // public void testInvalidPath() throws Exception { 169 | // this.mockMvc.perform(get("/inventory/iteeeee") 170 | // .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 171 | // .andExpect(status().isNotFound()) 172 | // .andDo(print()) 173 | // // FIXME: why????? 174 | //// .andExpect(content().contentType("application/json;charset=UTF-8")) 175 | // .andExpect(jsonPath("$.errorId", is("SVR_URI_002"))) 176 | // .andExpect(jsonPath("$.errorMessage", is("an invalid path was specified for path of URI."))) 177 | // .andDo(print()); 178 | // } 179 | 180 | @Test 181 | public void testReduce_validValue() throws Exception { 182 | final ObjectMapper objectMapper = new ObjectMapper(); 183 | final String json = objectMapper.writeValueAsString( 184 | Arrays.asList(new Item(1, "Hoge", 10000, 5, "desc", null, null, 0L)) 185 | ); 186 | this.mockMvc.perform(post("/inventory/update/") 187 | .contentType(MediaType.APPLICATION_JSON) 188 | .content(json)) 189 | .andExpect(status().isOk()) 190 | .andExpect(content().string("success")) 191 | .andDo(print()); 192 | } 193 | 194 | @Test 195 | public void testCheck_validValue() throws Exception { 196 | this.mockMvc.perform(get("/inventory/check/5/") 197 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 198 | .andExpect(status().isOk()) 199 | .andExpect(content().contentType("application/json;charset=UTF-8")) 200 | .andExpect(jsonPath("$.id", is(5))) 201 | .andExpect(jsonPath("$.name", is("ES2015 In Action"))) 202 | .andExpect(jsonPath("$.pict", nullValue())) 203 | .andDo(print()); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /inventory/src/test/java/com/inventory/repository/InventoryMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.inventory.repository; 2 | 3 | import com.inventory.config.MyBatisConfig; 4 | import com.inventory.config.TestDataSource; 5 | import com.inventory.domain.Category; 6 | import com.inventory.domain.Item; 7 | import com.inventory.domain.ReduceInfo; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.test.context.jdbc.Sql; 13 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 14 | 15 | import java.util.List; 16 | 17 | import static org.hamcrest.Matchers.*; 18 | import static org.junit.Assert.assertThat; 19 | 20 | @RunWith(SpringJUnit4ClassRunner.class) 21 | @SpringBootTest(classes = {TestDataSource.class, MyBatisConfig.class}) 22 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 23 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 24 | public class InventoryMapperTest { 25 | 26 | @Autowired 27 | public InventoryMapper sut; 28 | 29 | @Test 30 | public void testSelectItemsByCategoryId_nullValue() throws Exception { 31 | final List items = sut.selectItemsByCategoryId(null); 32 | assertThat(items, is(notNullValue())); 33 | assertThat(items.size(), is(9)); 34 | 35 | // FIXME: Please tell me that how to test using samePropertyValueAs(...) for nested(complex) bean. 36 | // assertThat(items.get(0), samePropertyValuesAs( 37 | // new Item(1, "Apple", new Category(1, "FOOD")) 38 | // )); 39 | Item item = items.get(0); 40 | assertThat(item.getId(), is(1)); 41 | assertThat(item.getName(), is("Apple")); 42 | // INSERT INTO ITEM (NAME, PRICE, UNIT, DESCRIPTION, PICT, CATEGORY_ID) 43 | // VALUES ('Apple', 110, 5, 'Apple is fruits', FILE_READ('classpath:pict/apple.jpeg'), 1); 44 | assertThat(item.getPrice(), is(110)); 45 | assertThat(item.getUnit(), is(5)); 46 | assertThat(item.getDescription(), is("Apple is fruits")); 47 | // TODO: Must assert value of pict. 48 | assertThat(item.getCategory(), samePropertyValuesAs(new Category(1, "FOOD"))); 49 | 50 | item = items.get(8); 51 | assertThat(item.getId(), is(9)); 52 | assertThat(item.getName(), is("STAR WARS")); 53 | assertThat(item.getCategory(), samePropertyValuesAs(new Category(3, "DVD"))); 54 | } 55 | 56 | @Test 57 | public void testSelectItemsByCategoryId_validValue() throws Exception { 58 | final List items = sut.selectItemsByCategoryId(1); 59 | assertThat(items, is(notNullValue())); 60 | assertThat(items.size(), is(3)); 61 | // FIXME: Please tell me that how to test using samePropertyValueAs(...) for nested(complex) bean. 62 | // assertThat(items.get(0), samePropertyValuesAs( 63 | // new Item(3, "Grape", new Category(1, "FOOD")) 64 | // )); 65 | final Item item = items.get(2); 66 | assertThat(item.getId(), is(3)); 67 | assertThat(item.getName(), is("Grape")); 68 | assertThat(item.getCategory(), samePropertyValuesAs(new Category(1, "FOOD"))); 69 | } 70 | 71 | @Test 72 | public void testSelectItemsByCategoryId_invalidValue() throws Exception { 73 | final List items = sut.selectItemsByCategoryId(Integer.MAX_VALUE); 74 | assertThat(items, is(notNullValue())); 75 | assertThat(items.size(), is(0)); 76 | } 77 | 78 | @Test 79 | public void testSelectItemById_validValue() throws Exception { 80 | final Item item = sut.selectItemById(3); 81 | assertThat(item, is(notNullValue())); 82 | assertThat(item.getId(), is(3)); 83 | assertThat(item.getName(), is("Grape")); 84 | assertThat(item.getCategory(), samePropertyValuesAs(new Category(1, "FOOD"))); 85 | } 86 | 87 | @Test 88 | public void testSelectItemById_invalidValue() throws Exception { 89 | final Item item = sut.selectItemById(Integer.MAX_VALUE); 90 | assertThat(item, is(nullValue())); 91 | } 92 | 93 | @Test 94 | public void testUpdate_validValue() throws Exception { 95 | Item item = sut.selectItemById(1); 96 | assertThat(item.getUnit(), is(5)); 97 | boolean result = sut.update(new ReduceInfo(1, 3, 0L)); 98 | item = sut.selectItemById(1); 99 | assertThat(item.getUnit(), is(3)); 100 | assertThat(result, is(true)); 101 | assertThat(item.getVersionno(), is(1L)); 102 | 103 | result = sut.update(new ReduceInfo(1, 3, 0L)); 104 | assertThat(result, is(false)); 105 | } 106 | 107 | @Test 108 | public void testUpdate_invalidValue() throws Exception { 109 | final Item item = sut.selectItemById(1); 110 | final boolean result = sut.update(new ReduceInfo(1, 3, item.getVersionno() + 1)); 111 | assertThat(result, is(false)); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /inventory/src/test/java/com/inventory/service/InventoryServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.inventory.service; 2 | 3 | import com.inventory.domain.Category; 4 | import com.inventory.domain.Item; 5 | import com.inventory.domain.ReduceInfo; 6 | import com.inventory.exception.InventoryLackingException; 7 | import com.inventory.exception.InventoryOptimisticException; 8 | import com.inventory.exception.ItemNotFoundException; 9 | import com.inventory.repository.InventoryMapper; 10 | import org.junit.Before; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | import org.junit.rules.ExpectedException; 14 | import org.junit.runner.RunWith; 15 | import org.mockito.InjectMocks; 16 | import org.mockito.Mock; 17 | import org.mockito.runners.MockitoJUnitRunner; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | import static org.hamcrest.Matchers.is; 24 | import static org.hamcrest.Matchers.nullValue; 25 | import static org.hamcrest.Matchers.samePropertyValuesAs; 26 | import static org.junit.Assert.*; 27 | import static org.mockito.Matchers.isNull; 28 | import static org.mockito.Mockito.when; 29 | 30 | @SpringBootTest 31 | @RunWith(MockitoJUnitRunner.class) 32 | public class InventoryServiceTest { 33 | 34 | @InjectMocks 35 | InventoryService sut; 36 | 37 | @Mock 38 | InventoryMapper inventoryMapper; 39 | 40 | @Rule 41 | public ExpectedException expectedException = ExpectedException.none(); 42 | 43 | @Before 44 | public void setUp() throws Exception { 45 | when(inventoryMapper.update(new ReduceInfo(1, 5, 0L))).thenReturn(true); 46 | when(inventoryMapper.selectItemById(1)).thenReturn(new Item(1, "Hoge", 10000, 10, "desc", null, new Category(1, "Bar"), 0L)); 47 | when(inventoryMapper.selectItemById(null)).thenReturn(null); 48 | when(inventoryMapper.selectItemsByCategoryId(1)).thenReturn(Arrays.asList(new Item(), new Item())); 49 | when(inventoryMapper.selectItemsByCategoryId(null)).thenReturn(Arrays.asList(new Item(), new Item(), new Item(), new Item())); 50 | } 51 | 52 | @Test 53 | public void testItems() throws Exception { 54 | assertThat(sut.items().size(), is(4)); 55 | } 56 | 57 | @Test 58 | public void testItems_validValue() throws Exception { 59 | assertThat(sut.items(1).size(), is(2)); 60 | } 61 | 62 | @Test 63 | public void testItems_invalidValue() throws Exception { 64 | assertThat(sut.items(Integer.MAX_VALUE).size(), is(0)); 65 | } 66 | 67 | @Test 68 | public void testItem_validValue() throws Exception { 69 | expectedException.expect(ItemNotFoundException.class); 70 | final Item item = sut.item(1); 71 | assertThat(item.getId(), is(1)); 72 | assertThat(item.getName(), is("Hoge")); 73 | assertThat(item.getCategory(), samePropertyValuesAs(new Category(1, "Bar"))); 74 | 75 | assertThat(sut.item(2), is(nullValue())); 76 | } 77 | 78 | @Test 79 | public void testItem_invalidValue() throws Exception { 80 | expectedException.expect(ItemNotFoundException.class); 81 | assertThat(sut.item(Integer.MAX_VALUE), is(nullValue())); 82 | } 83 | 84 | @Test 85 | public void testReduce_validValue() throws Exception { 86 | final Item item = sut.item(1); 87 | assertThat(item.getUnit(), is(10)); 88 | final String result = sut.reduce(Arrays.asList( 89 | new Item(1, "test", 10000, 5, "desc", null, null, 0L) 90 | )); 91 | assertThat(result, is("success")); 92 | } 93 | 94 | @Test 95 | public void testReduce_invalidValue() throws Exception { 96 | expectedException.expect(InventoryLackingException.class); 97 | final Item item = sut.item(1); 98 | assertThat(item.getUnit(), is(10)); 99 | final String result = sut.reduce(Arrays.asList( 100 | new Item(1, "test", 10000, 30, "desc", null, null, 0L) 101 | )); 102 | } 103 | 104 | @Test 105 | public void testReduce_invalidBoundaryValue() throws Exception { 106 | final Item item = sut.item(1); 107 | assertThat(item.getUnit(), is(10)); 108 | final String result = sut.reduce(Arrays.asList( 109 | new Item(1, "test", 10000, 10, "desc", null, null, 0L) 110 | )); 111 | } 112 | 113 | @Test 114 | public void testCheck_validValue() throws Exception { 115 | final Item item = sut.check(1); 116 | assertThat(item.getId(), is(1)); 117 | assertThat(item.getName(), is("Hoge")); 118 | assertThat(item.getCategory(), nullValue()); 119 | } 120 | 121 | @Test 122 | public void testReduce_invalidVersionNo() throws Exception { 123 | expectedException.expect(InventoryOptimisticException.class); 124 | final Item item = sut.item(1); 125 | final String result = sut.reduce(Arrays.asList( 126 | new Item(1, "test", 10000, 5, "desc", null, null, 999L) 127 | )); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /screenshots/bootadmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/screenshots/bootadmin.png -------------------------------------------------------------------------------- /screenshots/microservicedashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/screenshots/microservicedashboard.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'admin', 'discovery', 'inventory', 'apigw', 'shipping', 'storefrontend' 2 | -------------------------------------------------------------------------------- /shipping/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /shipping/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.4.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | group = 'com.example' 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | ext { 27 | springCloudVersion = 'Camden.SR5' 28 | } 29 | 30 | dependencies { 31 | compile('org.springframework.boot:spring-boot-starter-actuator') 32 | compile('org.springframework.cloud:spring-cloud-starter-eureka') 33 | compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1') 34 | runtime('org.springframework.boot:spring-boot-devtools') 35 | runtime('com.h2database:h2') 36 | testCompile('org.springframework.boot:spring-boot-starter-test') 37 | compile('org.jolokia:jolokia-core') 38 | compile('org.projectlombok:lombok') 39 | } 40 | 41 | dependencyManagement { 42 | imports { 43 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/ShippingApplication.java: -------------------------------------------------------------------------------- 1 | package com.shipping; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.context.ConfigurableApplicationContext; 7 | import org.springframework.web.servlet.DispatcherServlet; 8 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 | 10 | @EnableDiscoveryClient 11 | @SpringBootApplication 12 | @EnableWebMvc 13 | public class ShippingApplication { 14 | public static void main(String[] args) { 15 | final ConfigurableApplicationContext context = SpringApplication.run(ShippingApplication.class, args); 16 | DispatcherServlet dispatcherServlet = ((DispatcherServlet) context.getBean("dispatcherServlet")); 17 | dispatcherServlet.setThrowExceptionIfNoHandlerFound(true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/config/MyBatisConfig.java: -------------------------------------------------------------------------------- 1 | package com.shipping.config; 2 | 3 | import org.mybatis.spring.SqlSessionFactoryBean; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Lazy; 9 | 10 | import javax.sql.DataSource; 11 | 12 | @Configuration 13 | @MapperScan("com.shipping.repository") 14 | public class MyBatisConfig { 15 | 16 | private final DataSource dataSource; 17 | 18 | @Autowired 19 | @Lazy // FIXME: Workaround : The dependencies of some of the beans in the application context form a cycle: 20 | public MyBatisConfig(final DataSource dataSource) { 21 | this.dataSource = dataSource; 22 | } 23 | 24 | @Bean 25 | public SqlSessionFactoryBean sqlSessionFactoryBean() { 26 | final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 27 | sqlSessionFactoryBean.setDataSource(dataSource); 28 | return sqlSessionFactoryBean; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | package com.shipping.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.client.RestTemplate; 6 | 7 | @Configuration 8 | public class RestTemplateConfig { 9 | @Bean 10 | public RestTemplate restTemplate() { 11 | return new RestTemplate(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/controller/ShippingController.java: -------------------------------------------------------------------------------- 1 | package com.shipping.controller; 2 | 3 | import com.shipping.domain.OrderInfo; 4 | import com.shipping.service.ShippingService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | @CrossOrigin 11 | @Validated 12 | @RequestMapping("/shipping") 13 | @RestController 14 | public class ShippingController { 15 | 16 | private final ShippingService shippingService; 17 | 18 | @Autowired 19 | public ShippingController(final ShippingService shippingService) { 20 | this.shippingService = shippingService; 21 | } 22 | 23 | @PostMapping(value = "/order/") 24 | public String order(@Validated @RequestBody final OrderInfo orderInfo) { 25 | return shippingService.order(orderInfo); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/domain/Address.java: -------------------------------------------------------------------------------- 1 | package com.shipping.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.NotNull; 8 | import javax.validation.constraints.Size; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class Address { 14 | 15 | @NotNull 16 | private Long registerId; 17 | 18 | @NotNull 19 | private String zipCode; 20 | 21 | @NotNull 22 | @Size(min = 10, max = 50) 23 | private String location; 24 | 25 | @NotNull 26 | @Size(min = 10, max = 20) 27 | private String receiverName; 28 | } 29 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/domain/OrderInfo.java: -------------------------------------------------------------------------------- 1 | package com.shipping.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.Valid; 8 | import javax.validation.constraints.NotNull; 9 | import javax.validation.constraints.Size; 10 | import java.util.List; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class OrderInfo { 16 | 17 | @NotNull 18 | private Long registerId; 19 | 20 | @NotNull 21 | @Valid 22 | private List orderedItem; 23 | 24 | @NotNull 25 | @Size(min = 10, max = 20) 26 | private String senderName; 27 | 28 | @NotNull 29 | @Valid 30 | private Address address; 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/domain/OrderedItem.java: -------------------------------------------------------------------------------- 1 | package com.shipping.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.validation.constraints.Min; 8 | import javax.validation.constraints.NotNull; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class OrderedItem { 14 | 15 | @NotNull 16 | private Long registerId; 17 | 18 | @NotNull 19 | @Min(1) 20 | private Integer id; 21 | 22 | @NotNull 23 | @Min(1) 24 | private Integer unit; 25 | 26 | private Long versionno; 27 | } 28 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/exception/GenerateIdException.java: -------------------------------------------------------------------------------- 1 | package com.shipping.exception; 2 | 3 | public class GenerateIdException extends RuntimeException { 4 | private static final String DEFAULTMESSAGE = "exception occurs during generating id."; 5 | public GenerateIdException() { 6 | super(DEFAULTMESSAGE); 7 | } 8 | 9 | public GenerateIdException(final RuntimeException e) { 10 | super(DEFAULTMESSAGE, e); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/exception/ItemNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.shipping.exception; 2 | 3 | public class ItemNotFoundException extends RuntimeException { 4 | public ItemNotFoundException() { 5 | super("could not find specified item in the inventory service."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/exception/ItemUnitLackingException.java: -------------------------------------------------------------------------------- 1 | package com.shipping.exception; 2 | 3 | public class ItemUnitLackingException extends RuntimeException { 4 | public ItemUnitLackingException() { 5 | super("there is not enough amount."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/exception/NetworkException.java: -------------------------------------------------------------------------------- 1 | package com.shipping.exception; 2 | 3 | public class NetworkException extends RuntimeException { 4 | public NetworkException(Throwable e) { 5 | super(e); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/exception/ReduceFailedException.java: -------------------------------------------------------------------------------- 1 | package com.shipping.exception; 2 | 3 | public class ReduceFailedException extends RuntimeException { 4 | public ReduceFailedException() { 5 | super("your order was not processed."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/generator/Generator.java: -------------------------------------------------------------------------------- 1 | package com.shipping.generator; 2 | 3 | public interface Generator { 4 | Long generate(); 5 | } 6 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/generator/RegisterIdGenerator.java: -------------------------------------------------------------------------------- 1 | package com.shipping.generator; 2 | 3 | import com.shipping.exception.GenerateIdException; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.text.SimpleDateFormat; 7 | import java.util.Date; 8 | 9 | @Component 10 | public class RegisterIdGenerator implements Generator { 11 | 12 | private static final String ID_PATTERN = "YYYYMMDDHHMMSSS"; 13 | 14 | @Override 15 | public Long generate() { 16 | final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ID_PATTERN); 17 | final String formattedValue = simpleDateFormat.format(new Date()); 18 | try { 19 | return Long.parseLong(formattedValue); 20 | } catch (final NumberFormatException e) { 21 | throw new GenerateIdException(e); 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/handler/ErrorInformation.java: -------------------------------------------------------------------------------- 1 | package com.shipping.handler; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ErrorInformation { 7 | 8 | private final String errorId; 9 | private final String errorMessage; 10 | private final String solution; 11 | 12 | private ErrorInformation(final Builder builder) { 13 | errorId = builder.errorId; 14 | errorMessage = builder.errorMessage; 15 | solution = builder.solution; 16 | } 17 | 18 | public static class Builder { 19 | 20 | private final String errorId; 21 | private final String errorMessage; 22 | private String solution; 23 | 24 | public Builder(final String errorId, final String errorMessage) { 25 | this.errorId = errorId; 26 | this.errorMessage = errorMessage; 27 | } 28 | 29 | public Builder countermeasure(final String solution) { 30 | this.solution = solution; 31 | return this; 32 | } 33 | 34 | public ErrorInformation build() { 35 | return new ErrorInformation(this); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/handler/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.shipping.handler; 2 | 3 | import com.shipping.exception.ItemNotFoundException; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.web.bind.MethodArgumentNotValidException; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.ResponseStatus; 9 | import org.springframework.web.bind.annotation.RestControllerAdvice; 10 | 11 | @RestControllerAdvice 12 | @Component 13 | public class GlobalExceptionHandler { 14 | 15 | @ExceptionHandler 16 | @ResponseStatus(HttpStatus.BAD_REQUEST) 17 | public ErrorInformation handle(final MethodArgumentNotValidException e) { 18 | return new ErrorInformation.Builder("SHIPPING_SVR_001", "an invalid value was specified for posted json.").build(); 19 | } 20 | 21 | @ExceptionHandler 22 | @ResponseStatus(HttpStatus.BAD_REQUEST) 23 | public ErrorInformation handle(final ItemNotFoundException e) { 24 | return new ErrorInformation.Builder("SHIPPING_SVR_002", e.getMessage()).build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/repository/AddressMapper.java: -------------------------------------------------------------------------------- 1 | package com.shipping.repository; 2 | 3 | import com.shipping.domain.Address; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface AddressMapper { 8 | 9 | void insertAddress(Address address); 10 | } 11 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/repository/OrderHistoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.shipping.repository; 2 | 3 | import com.shipping.domain.OrderInfo; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface OrderHistoryMapper { 8 | 9 | void insertOrderHistory(OrderInfo orderInfo); 10 | } 11 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/repository/OrderedItemMapper.java: -------------------------------------------------------------------------------- 1 | package com.shipping.repository; 2 | 3 | import com.shipping.domain.OrderedItem; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface OrderedItemMapper { 8 | void insertOrderedItem(OrderedItem orderedItem); 9 | } 10 | -------------------------------------------------------------------------------- /shipping/src/main/java/com/shipping/service/ShippingService.java: -------------------------------------------------------------------------------- 1 | package com.shipping.service; 2 | 3 | import com.shipping.domain.OrderInfo; 4 | import com.shipping.domain.OrderedItem; 5 | import com.shipping.exception.ItemNotFoundException; 6 | import com.shipping.exception.ItemUnitLackingException; 7 | import com.shipping.exception.NetworkException; 8 | import com.shipping.exception.ReduceFailedException; 9 | import com.shipping.generator.Generator; 10 | import com.shipping.repository.AddressMapper; 11 | import com.shipping.repository.OrderHistoryMapper; 12 | import com.shipping.repository.OrderedItemMapper; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.transaction.annotation.Transactional; 16 | import org.springframework.web.client.HttpClientErrorException; 17 | import org.springframework.web.client.ResourceAccessException; 18 | import org.springframework.web.client.RestTemplate; 19 | 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | @Service 25 | @Transactional 26 | public class ShippingService { 27 | 28 | private RestTemplate restTemplate; 29 | 30 | private AddressMapper addressMapper; 31 | 32 | private OrderedItemMapper orderedItemMapper; 33 | 34 | private OrderHistoryMapper orderHistoryMapper; 35 | 36 | private Generator generator; 37 | 38 | private static final String ID_PATTERN = "YYYYMMDDHHSSMMM"; 39 | 40 | @Autowired 41 | public ShippingService(final RestTemplate restTemplate, 42 | final AddressMapper addressMapper, 43 | final OrderedItemMapper orderedItemMapper, 44 | final OrderHistoryMapper orderHistoryMapper, 45 | final Generator generator) { 46 | this.restTemplate = restTemplate; 47 | this.addressMapper = addressMapper; 48 | this.orderedItemMapper = orderedItemMapper; 49 | this.orderHistoryMapper = orderHistoryMapper; 50 | this.generator = generator; 51 | } 52 | 53 | public String order(final OrderInfo orderInfo) { 54 | orderInfo.getOrderedItem().forEach(orderedItem -> { 55 | checkInventory(orderedItem); 56 | }); 57 | updateRegisterId(orderInfo); 58 | registerOrderInfo(orderInfo); 59 | 60 | return "success"; 61 | } 62 | 63 | private void updateRegisterId(final OrderInfo orderInfo) { 64 | final Long registerId = generator.generate(); 65 | orderInfo.setRegisterId(registerId); 66 | orderInfo.getAddress().setRegisterId(registerId); 67 | orderInfo.getOrderedItem().forEach(orderedItem -> orderedItem.setRegisterId(registerId)); 68 | orderInventory(orderInfo.getOrderedItem()); 69 | } 70 | 71 | private void registerOrderInfo(final OrderInfo orderInfo) { 72 | addressMapper.insertAddress(orderInfo.getAddress()); 73 | orderInfo.getOrderedItem().forEach(orderedItem -> orderedItemMapper.insertOrderedItem(orderedItem)); 74 | orderHistoryMapper.insertOrderHistory(orderInfo); 75 | } 76 | 77 | private void orderInventory(final List orderedItem) { 78 | final String result = restTemplate.postForObject("http://localhost:8200/inventory/update/", 79 | orderedItem, 80 | String.class); 81 | if (!"success".equals(result)) { 82 | throw new ReduceFailedException(); 83 | } 84 | } 85 | 86 | private void checkInventory(final OrderedItem orderedItem) { 87 | final HashMap params = new HashMap<>(); 88 | params.put("itemId", orderedItem.getId().toString()); 89 | 90 | try { 91 | Map result = 92 | restTemplate.getForObject("http://localhost:8200/inventory/check/{itemId}/", Map.class, params); 93 | if (result.containsKey("errorId")) { 94 | if ("SVR_URI_010".equals(result.get("errorId"))) { 95 | throw new ItemNotFoundException(); 96 | } 97 | } 98 | 99 | if (Integer.parseInt(result.get("unit").toString()) < orderedItem.getUnit()) { 100 | throw new ItemUnitLackingException(); 101 | } 102 | orderedItem.setVersionno(Long.parseLong(result.get("versionno").toString())); 103 | } catch (ResourceAccessException | HttpClientErrorException e) { 104 | throw new NetworkException(e); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /shipping/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8210 3 | 4 | spring: 5 | application: 6 | name: shipping 7 | output: 8 | ansi: 9 | enabled: ALWAYS 10 | 11 | eureka: 12 | client: 13 | serviceUrl: 14 | defaultZone: http://localhost:8100/eureka/ 15 | 16 | info: 17 | name: shipping 18 | 19 | logging: 20 | file: logfile/shipping.log 21 | -------------------------------------------------------------------------------- /shipping/src/main/resources/com/shipping/repository/AddressMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | INSERT 10 | INTO ADDR 11 | (REGISTERID, ZIPCODE, LOCATION, RECEIVERNAME) 12 | VALUES 13 | (#{registerId}, #{zipCode}, #{location}, #{receiverName}) 14 | 15 | 16 | -------------------------------------------------------------------------------- /shipping/src/main/resources/com/shipping/repository/OrderHistoryMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | INSERT INTO ORDERHIST 10 | (REGISTERID, SENDERNAME, ORDEREDITEM_ID, ADDR_ID) 11 | VALUES 12 | (#{registerId}, #{senderName}, #{registerId}, #{registerId}) 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /shipping/src/main/resources/com/shipping/repository/OrderedItemMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | INSERT 10 | INTO ORDEREDITEM 11 | (REGISTERID, ID, UNIT) 12 | VALUES 13 | (#{registerId}, #{id}, #{unit}) 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /shipping/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO ADDR (REGISTERID, ZIPCODE, LOCATION, RECEIVERNAME) VALUES (201712150912233, '123-4567', 'Tokyo-Fuchu', 'SCOTT'); 2 | INSERT INTO ORDEREDITEM (REGISTERID, ID, UNIT) VALUES (201712150912233, 1, 5); 3 | INSERT INTO ORDERHIST (REGISTERID, SENDERNAME, ORDEREDITEM_ID, ADDR_ID) VALUES (201712150912233, 'TIGER', 201712150912233, 201712150912233); 4 | -------------------------------------------------------------------------------- /shipping/src/main/resources/drop.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ADDR; 2 | DROP TABLE IF EXISTS ORDEREDITEM; 3 | DROP TABLE IF EXISTS ORDERHIST; 4 | -------------------------------------------------------------------------------- /shipping/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS ADDR ( 2 | REGISTERID BIGINT PRIMARY KEY NOT NULL, 3 | ZIPCODE VARCHAR(10) NOT NULL, 4 | LOCATION VARCHAR(50) NOT NULL, 5 | RECEIVERNAME VARCHAR(20) NOT NULL 6 | ); 7 | 8 | CREATE TABLE IF NOT EXISTS ORDEREDITEM ( 9 | REGISTERID BIGINT PRIMARY KEY NOT NULL, 10 | ID INT(100) NOT NULL, 11 | UNIT INT(100) NOT NULL 12 | ); 13 | 14 | -- TODO: I might add 'timestamp' to ORDEREDITEM table. 15 | 16 | CREATE TABLE IF NOT EXISTS ORDERHIST ( 17 | REGISTERID BIGINT PRIMARY KEY NOT NULL, 18 | SENDERNAME VARCHAR(20) NOT NULL, 19 | ORDEREDITEM_ID BIGINT NOT NULL, 20 | ADDR_ID BIGINT NOT NULL, 21 | FOREIGN KEY (ORDEREDITEM_ID) REFERENCES ORDEREDITEM(REGISTERID), 22 | FOREIGN KEY (ADDR_ID) REFERENCES ADDR(REGISTERID) 23 | ); 24 | 25 | -- TODO: I may fix DDL to record bought multi items. 26 | 27 | -- Note: To store data likes timestamp as primary key, so I defined primary key using BIGINT. 28 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/config/TestDataSource.java: -------------------------------------------------------------------------------- 1 | package com.shipping.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.jdbc.core.JdbcTemplate; 6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 7 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 8 | 9 | import javax.sql.DataSource; 10 | 11 | @Configuration 12 | public class TestDataSource { 13 | @Bean 14 | public DataSource dataSource() { 15 | return new EmbeddedDatabaseBuilder() 16 | .setType(EmbeddedDatabaseType.H2) 17 | .build(); 18 | } 19 | 20 | @Bean 21 | public JdbcTemplate jdbcTemplate() { 22 | return new JdbcTemplate(dataSource()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/controller/ShippingControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.shipping.controller; 2 | 3 | import com.shipping.json.TestData; 4 | import com.shipping.service.ShippingService; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.mockito.InjectMocks; 9 | import org.mockito.Mock; 10 | import org.mockito.runners.MockitoJUnitRunner; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import static org.hamcrest.MatcherAssert.assertThat; 14 | import static org.hamcrest.Matchers.is; 15 | import static org.mockito.Mockito.when; 16 | 17 | @SpringBootTest 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class ShippingControllerTest { 20 | 21 | @InjectMocks 22 | ShippingController sut; 23 | 24 | @Mock 25 | ShippingService shippingService; 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | when(shippingService.order(TestData.orderInfo)).thenReturn("success"); 30 | } 31 | 32 | @Test 33 | public void order() throws Exception { 34 | assertThat(sut.order(TestData.orderInfo), is("success")); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/generator/RegisterIdGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.shipping.generator; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | import static org.hamcrest.Matchers.is; 10 | import static org.hamcrest.Matchers.notNullValue; 11 | import static org.junit.Assert.*; 12 | 13 | @RunWith(SpringJUnit4ClassRunner.class) 14 | @SpringBootTest(classes = RegisterIdGenerator.class) 15 | public class RegisterIdGeneratorTest { 16 | 17 | @Autowired 18 | Generator sut; 19 | 20 | @Test 21 | public void testGenerate() throws Exception { 22 | final Long id = sut.generate(); 23 | assertThat(id, notNullValue()); 24 | System.out.println(id); 25 | assertThat(id.toString().length(), is(15)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/integration/ShippingApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.shipping.integration; 2 | 3 | import com.shipping.ShippingApplication; 4 | import com.shipping.config.MyBatisConfig; 5 | import com.shipping.config.TestDataSource; 6 | import com.shipping.json.TestData; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.test.context.ContextConfiguration; 14 | import org.springframework.test.context.jdbc.Sql; 15 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 16 | import org.springframework.test.context.web.WebAppConfiguration; 17 | import org.springframework.test.web.servlet.MockMvc; 18 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 19 | import org.springframework.web.context.WebApplicationContext; 20 | 21 | import static org.hamcrest.Matchers.is; 22 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 23 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 24 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 25 | 26 | @RunWith(SpringJUnit4ClassRunner.class) 27 | @SpringBootTest(classes = {TestDataSource.class, MyBatisConfig.class}) 28 | @WebAppConfiguration 29 | @ContextConfiguration(classes = ShippingApplication.class) 30 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 31 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 32 | public class ShippingApplicationTests { 33 | 34 | MockMvc mockMvc; 35 | 36 | @Autowired 37 | WebApplicationContext webApplicationContext; 38 | 39 | @Before 40 | public void setUp() throws Exception { 41 | this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); 42 | } 43 | 44 | @Test 45 | public void testOrder_validValue() throws Exception { 46 | this.mockMvc.perform(post("/shipping/order/") 47 | .contentType(MediaType.APPLICATION_JSON) 48 | .content(TestData.getTestData().get("ORDER_INFO_VALID").getBytes()) 49 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 50 | .andExpect(status().isOk()) 51 | .andExpect(content().contentType("application/json;charset=UTF-8")) 52 | .andDo(print()); 53 | } 54 | 55 | @Test 56 | public void testOrder_invalidValue_item() throws Exception { 57 | this.mockMvc.perform(post("/shipping/order/") 58 | .contentType(MediaType.APPLICATION_JSON) 59 | .content(TestData.getTestData().get("ORDER_INFO_INVALID").getBytes()) 60 | .accept(MediaType.parseMediaType("application/json;charset=UTF-8"))) 61 | .andExpect(status().isBadRequest()) 62 | .andExpect(jsonPath("$.errorId", is("SHIPPING_SVR_002"))) 63 | .andExpect(jsonPath("$.errorMessage", is("could not find specified item in the inventory service."))) 64 | .andDo(print()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/json/TestData.java: -------------------------------------------------------------------------------- 1 | package com.shipping.json; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.shipping.domain.Address; 6 | import com.shipping.domain.OrderInfo; 7 | import com.shipping.domain.OrderedItem; 8 | 9 | import java.util.Arrays; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public class TestData { 14 | 15 | public static final OrderInfo orderInfo = 16 | new OrderInfo( 17 | 1L, 18 | Arrays.asList(new OrderedItem(1L, 1, 1, 0L)), "TestTestTest", 19 | new Address(1L, "123-4567", "TestLocTestLoc", "NameNameName") 20 | ); 21 | public static final OrderInfo orderInfo_invalid = 22 | new OrderInfo( 23 | 1L, 24 | Arrays.asList(new OrderedItem(1L, 99, 1, 0L)), "TestTestTest", 25 | new Address(1L, "123-4567", "TestLocTestLoc", "NameNameName") 26 | ); 27 | 28 | public static Map getTestData() throws JsonProcessingException { 29 | return new HashMap() { 30 | { 31 | final ObjectMapper objectMapper = new ObjectMapper(); 32 | put("ORDER_INFO_VALID", objectMapper.writeValueAsString(orderInfo)); 33 | put("ORDER_INFO_INVALID", objectMapper.writeValueAsString(orderInfo_invalid)); 34 | } 35 | }; 36 | } 37 | 38 | public static void main(String[] args) { 39 | try { 40 | System.out.println(getTestData()); 41 | } catch (JsonProcessingException e) { 42 | e.printStackTrace(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/repository/AddressMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.shipping.repository; 2 | 3 | import com.shipping.config.MyBatisConfig; 4 | import com.shipping.config.TestDataSource; 5 | import com.shipping.domain.Address; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.dao.DuplicateKeyException; 13 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 14 | import org.springframework.jdbc.core.JdbcTemplate; 15 | import org.springframework.test.context.jdbc.Sql; 16 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 17 | 18 | import static org.hamcrest.Matchers.is; 19 | import static org.hamcrest.Matchers.samePropertyValuesAs; 20 | import static org.junit.Assert.*; 21 | 22 | @RunWith(SpringJUnit4ClassRunner.class) 23 | @SpringBootTest(classes = {TestDataSource.class, MyBatisConfig.class}) 24 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 25 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 26 | public class AddressMapperTest { 27 | 28 | @Autowired 29 | private JdbcTemplate jdbcTemplate; 30 | 31 | @Autowired 32 | public AddressMapper sut; 33 | 34 | @Rule 35 | public ExpectedException expectedException = ExpectedException.none(); 36 | 37 | @Test 38 | public void testInsert_valid() throws Exception { 39 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ADDR").size(), is(1)); 40 | final Address address = new Address(1L, "123-4567", "testLocation", "testName"); 41 | sut.insertAddress(address); 42 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ADDR").size(), is(2)); 43 | 44 | final Address address1 = jdbcTemplate.queryForObject("SELECT * FROM ADDR WHERE REGISTERID = 1", new BeanPropertyRowMapper
(Address.class)); 45 | assertThat(address1, is(samePropertyValuesAs(address))); 46 | } 47 | 48 | @Test 49 | public void testInsert_invalid() throws Exception { 50 | expectedException.expect(DuplicateKeyException.class); 51 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ADDR").size(), is(1)); 52 | final Address address = new Address(1L, "123-4567", "testLocation", "testName"); 53 | sut.insertAddress(address); 54 | sut.insertAddress(address); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/repository/OrderHistoryMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.shipping.repository; 2 | 3 | import com.shipping.config.MyBatisConfig; 4 | import com.shipping.config.TestDataSource; 5 | import com.shipping.domain.OrderInfo; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.dao.DuplicateKeyException; 13 | import org.springframework.jdbc.core.JdbcTemplate; 14 | import org.springframework.test.context.jdbc.Sql; 15 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | import static org.hamcrest.Matchers.is; 21 | import static org.junit.Assert.*; 22 | 23 | @RunWith(SpringJUnit4ClassRunner.class) 24 | @SpringBootTest(classes = {TestDataSource.class, MyBatisConfig.class}) 25 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 26 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 27 | public class OrderHistoryMapperTest { 28 | 29 | @Autowired 30 | private JdbcTemplate jdbcTemplate; 31 | 32 | @Autowired 33 | public OrderHistoryMapper sut; 34 | 35 | @Rule 36 | public ExpectedException expectedException = ExpectedException.none(); 37 | 38 | @Test 39 | public void testInsert_valid() throws Exception { 40 | final List sqls = Arrays.asList( 41 | "INSERT INTO ADDR (REGISTERID, ZIPCODE, LOCATION, RECEIVERNAME) VALUES (99, '123-4567', 'Tokyo-Fuchu', 'SCOTT');", 42 | "INSERT INTO ORDEREDITEM (REGISTERID, ID, UNIT) VALUES (99, 1, 5);"); 43 | 44 | sqls.forEach(s -> jdbcTemplate.execute(s)); 45 | 46 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDERHIST").size(), is(1)); 47 | final OrderInfo orderInfo = new OrderInfo(99L, null, "TestName", null); 48 | sut.insertOrderHistory(orderInfo); 49 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDERHIST").size(), is(2)); 50 | } 51 | 52 | @Test 53 | public void testInsert_invalid() throws Exception { 54 | expectedException.expect(DuplicateKeyException.class); 55 | final List sqls = Arrays.asList( 56 | "INSERT INTO ADDR (REGISTERID, ZIPCODE, LOCATION, RECEIVERNAME) VALUES (99, '123-4567', 'Tokyo-Fuchu', 'SCOTT');", 57 | "INSERT INTO ORDEREDITEM (REGISTERID, ID, UNIT) VALUES (99, 1, 5);"); 58 | 59 | sqls.forEach(s -> jdbcTemplate.execute(s)); 60 | 61 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDERHIST").size(), is(1)); 62 | final OrderInfo orderInfo = new OrderInfo(99L, null, "TestName", null); 63 | sut.insertOrderHistory(orderInfo); 64 | sut.insertOrderHistory(orderInfo); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/repository/OrderedItemMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.shipping.repository; 2 | 3 | import com.shipping.config.MyBatisConfig; 4 | import com.shipping.config.TestDataSource; 5 | import com.shipping.domain.OrderedItem; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.dao.DuplicateKeyException; 13 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 14 | import org.springframework.jdbc.core.JdbcTemplate; 15 | import org.springframework.test.context.jdbc.Sql; 16 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 17 | 18 | import static org.hamcrest.Matchers.is; 19 | import static org.hamcrest.Matchers.samePropertyValuesAs; 20 | import static org.junit.Assert.*; 21 | 22 | @RunWith(SpringJUnit4ClassRunner.class) 23 | @SpringBootTest(classes = {TestDataSource.class, MyBatisConfig.class}) 24 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 25 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 26 | public class OrderedItemMapperTest { 27 | 28 | @Autowired 29 | private JdbcTemplate jdbcTemplate; 30 | 31 | @Autowired 32 | public OrderedItemMapper sut; 33 | 34 | @Rule 35 | public ExpectedException expectedException = ExpectedException.none(); 36 | 37 | @Test 38 | public void testInsert_valid() throws Exception { 39 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDEREDITEM").size(), is(1)); 40 | final OrderedItem orderedItem = new OrderedItem(201712150999999L, 1, 5, null); 41 | sut.insertOrderedItem(orderedItem); 42 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDEREDITEM").size(), is(2)); 43 | 44 | final OrderedItem orderedItem1 = jdbcTemplate.queryForObject("SELECT * FROM ORDEREDITEM WHERE REGISTERID = 201712150999999", new BeanPropertyRowMapper<>(OrderedItem.class)); 45 | assertThat(orderedItem1, samePropertyValuesAs(orderedItem)); 46 | } 47 | 48 | @Test 49 | public void testInsert_invalid() throws Exception { 50 | expectedException.expect(DuplicateKeyException.class); 51 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDEREDITEM").size(), is(1)); 52 | final OrderedItem orderedItem = new OrderedItem(201712150999999L, 1, 5, 0L); 53 | sut.insertOrderedItem(orderedItem); 54 | sut.insertOrderedItem(orderedItem); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /shipping/src/test/java/com/shipping/service/ShippingServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.shipping.service; 2 | 3 | import com.shipping.config.MyBatisConfig; 4 | import com.shipping.config.TestDataSource; 5 | import com.shipping.domain.Address; 6 | import com.shipping.domain.OrderInfo; 7 | import com.shipping.domain.OrderedItem; 8 | import com.shipping.exception.ItemNotFoundException; 9 | import com.shipping.exception.ItemUnitLackingException; 10 | import com.shipping.exception.NetworkException; 11 | import com.shipping.generator.RegisterIdGenerator; 12 | import org.junit.After; 13 | import org.junit.Before; 14 | import org.junit.Rule; 15 | import org.junit.Test; 16 | import org.junit.rules.ExpectedException; 17 | import org.junit.runner.RunWith; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.boot.test.context.SpringBootTest; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | import org.springframework.http.HttpMethod; 23 | import org.springframework.http.HttpStatus; 24 | import org.springframework.http.MediaType; 25 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 26 | import org.springframework.jdbc.core.JdbcTemplate; 27 | import org.springframework.test.context.jdbc.Sql; 28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 29 | import org.springframework.test.web.client.MockRestServiceServer; 30 | import org.springframework.web.client.RestTemplate; 31 | 32 | import java.util.Arrays; 33 | 34 | import static org.hamcrest.MatcherAssert.assertThat; 35 | import static org.hamcrest.Matchers.*; 36 | import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; 37 | import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; 38 | import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; 39 | import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; 40 | 41 | @RunWith(SpringJUnit4ClassRunner.class) 42 | @SpringBootTest(classes = { 43 | RestConfiguration.class, 44 | ShippingService.class, 45 | TestDataSource.class, 46 | MyBatisConfig.class, 47 | RegisterIdGenerator.class 48 | }) 49 | @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, 50 | scripts = {"classpath:drop.sql", "classpath:schema.sql", "classpath:data.sql"}) 51 | public class ShippingServiceTest { 52 | 53 | @Autowired 54 | ShippingService sut; 55 | 56 | @Rule 57 | public ExpectedException expectedException = ExpectedException.none(); 58 | 59 | @Autowired 60 | RestTemplate restTemplate; 61 | 62 | @Autowired 63 | JdbcTemplate jdbcTemplate; 64 | 65 | private MockRestServiceServer mockRestServiceServer; 66 | 67 | @Before 68 | public void setUp() throws Exception { 69 | mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build(); 70 | } 71 | 72 | @After 73 | public void tearDown() throws Exception { 74 | mockRestServiceServer.reset(); 75 | } 76 | 77 | @Test 78 | public void order_valid() throws Exception { 79 | mockRestServiceServer.expect(requestTo("http://localhost:8200/inventory/check/1/")) 80 | .andExpect(method(HttpMethod.GET)) 81 | .andRespond( 82 | withSuccess("{\"registerId\":1,\"name\":\"Apple\",\"price\":110,\"unit\":5,\"description\":null,\"pict\":null,\"category\":null,\"versionno\":0}", 83 | MediaType.APPLICATION_JSON)); 84 | mockRestServiceServer.expect(requestTo("http://localhost:8200/inventory/update/")) 85 | .andExpect(method(HttpMethod.POST)) 86 | .andRespond(withSuccess("success", MediaType.APPLICATION_JSON)); 87 | 88 | final Address address = new Address(201701121606954L, "test", "test", "test"); 89 | final OrderedItem orderedItem = new OrderedItem(201701121606954L, 1, 1, 0L); 90 | final OrderInfo orderInfo = new OrderInfo( 91 | 201701121606954L, 92 | Arrays.asList(orderedItem), 93 | "test", 94 | address); 95 | sut.order(orderInfo); 96 | 97 | final Address address1 = jdbcTemplate.queryForObject("SELECT * FROM ADDR WHERE REGISTERID = " + orderInfo.getRegisterId(), new BeanPropertyRowMapper<>(Address.class)); 98 | assertThat(address1, is(samePropertyValuesAs(address))); 99 | 100 | final OrderedItem orderedItem1 = jdbcTemplate.queryForObject("SELECT * FROM ORDEREDITEM WHERE REGISTERID = " + orderInfo.getRegisterId(), new BeanPropertyRowMapper<>(OrderedItem.class)); 101 | assertThat(orderedItem1.getRegisterId(), is(orderInfo.getRegisterId())); 102 | assertThat(orderedItem1.getId(), is(1)); 103 | assertThat(orderedItem1.getUnit(), is(1)); 104 | 105 | assertThat(jdbcTemplate.queryForList("SELECT * FROM ORDERHIST").size(), is(2)); 106 | mockRestServiceServer.verify(); 107 | } 108 | 109 | @Test 110 | public void order_invalid_wrongItemKey() throws Exception { 111 | mockRestServiceServer.expect(requestTo("http://localhost:8200/inventory/check/999/")) 112 | .andExpect(method(HttpMethod.GET)) 113 | .andRespond( 114 | withSuccess("{\"errorId\":\"SVR_URI_010\",\"errorMessage\":\"could not find specified item in the inventory service.\",\"solution\":null}", 115 | MediaType.APPLICATION_JSON)); 116 | expectedException.expect(ItemNotFoundException.class); 117 | final OrderInfo orderInfo = new OrderInfo( 118 | 1L, 119 | Arrays.asList(new OrderedItem(1L, 999, 3, 0L)), 120 | "test", 121 | null); 122 | sut.order(orderInfo); 123 | } 124 | 125 | @Test 126 | public void order_invalid_network() throws Exception { 127 | mockRestServiceServer.expect(requestTo("http://localhost:8200/inventory/check/1/")) 128 | .andRespond(withStatus(HttpStatus.NOT_FOUND)); 129 | expectedException.expect(NetworkException.class); 130 | final OrderInfo orderInfo = new OrderInfo( 131 | 1L, 132 | Arrays.asList(new OrderedItem(1L, 1, 3, 0L)), 133 | "test", 134 | null); 135 | sut.order(orderInfo); 136 | } 137 | 138 | @Test 139 | public void order_invalid_lacking_item() throws Exception { 140 | mockRestServiceServer.expect(requestTo("http://localhost:8200/inventory/check/1/")) 141 | .andExpect(method(HttpMethod.GET)) 142 | .andRespond( 143 | withSuccess("{\"registerId\":1,\"name\":\"Apple\",\"price\":110,\"unit\":5,\"description\":null,\"pict\":null,\"category\":null,\"versionno\":0}", 144 | MediaType.APPLICATION_JSON)); 145 | expectedException.expect(ItemUnitLackingException.class); 146 | final OrderInfo orderInfo = new OrderInfo( 147 | 1L, 148 | Arrays.asList(new OrderedItem(1L, 1, 6, 0L)), 149 | "test", 150 | null); 151 | sut.order(orderInfo); 152 | } 153 | } 154 | 155 | @Configuration 156 | class RestConfiguration { 157 | @Bean 158 | RestTemplate restTemplate() { 159 | return new RestTemplate(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /storefrontend/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /storefrontend/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.4.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'eclipse' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | group = 'com.example' 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | ext { 27 | springCloudVersion = 'Camden.SR5' 28 | } 29 | 30 | dependencies { 31 | compile('org.springframework.boot:spring-boot-starter-actuator') 32 | compile('org.springframework.cloud:spring-cloud-starter-eureka') 33 | compile('org.springframework.boot:spring-boot-starter-thymeleaf') 34 | compile('org.springframework.boot:spring-boot-starter-web') 35 | runtime('org.springframework.boot:spring-boot-devtools') 36 | runtime('com.h2database:h2') 37 | testCompile('org.springframework.boot:spring-boot-starter-test') 38 | compile('org.webjars:bootstrap:3.3.7-1') 39 | compile('org.webjars:jquery:3.1.1') 40 | compile('org.jolokia:jolokia-core') 41 | } 42 | 43 | dependencyManagement { 44 | imports { 45 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /storefrontend/src/main/java/com/storefrontend/StorefrontendApplication.java: -------------------------------------------------------------------------------- 1 | package com.storefrontend; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @EnableDiscoveryClient 8 | @SpringBootApplication 9 | public class StorefrontendApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(StorefrontendApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /storefrontend/src/main/java/com/storefrontend/controller/StoreFrontEndController.java: -------------------------------------------------------------------------------- 1 | package com.storefrontend.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | @Controller 7 | public class StoreFrontEndController { 8 | @RequestMapping("/") 9 | public String index() { 10 | return "index"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8300 3 | 4 | spring: 5 | application: 6 | name: storefrontend 7 | output: 8 | ansi: 9 | enabled: ALWAYS 10 | 11 | eureka: 12 | client: 13 | serviceUrl: 14 | defaultZone: http://localhost:8100/eureka/ 15 | 16 | info: 17 | name: storefrontend 18 | 19 | logging: 20 | file: logfile/storefrontend.log 21 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "main.css": "static/css/main.ce273286.css", 3 | "main.css.map": "static/css/main.ce273286.css.map", 4 | "main.js": "static/js/main.301a64e3.js", 5 | "main.js.map": "static/js/main.301a64e3.js.map" 6 | } -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} 6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */ -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/css/main.ce273286.css: -------------------------------------------------------------------------------- 1 | body{margin:0;padding:0;font-family:sans-serif}.footer-navbar-text-center{width:100%;text-align:center}.custom-image{width:300px;height:300px!important}.ratings{padding-right:10px;padding-left:10px;color:#d17581}.margin-left-sm{margin-left:10px} 2 | /*# sourceMappingURL=main.ce273286.css.map*/ -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/css/main.ce273286.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.css","components/footer/Footer.css","components/item/Item.css"],"names":[],"mappings":"AAAA,KACE,SACA,UACA,sBAAwB,CCH1B,2BACE,WACA,iBAAkB,CCFpB,cACE,YACD,sBAAwB,CAEzB,SACC,mBACA,kBACA,aAAe,CAEhB,gBACC,gBAAkB","file":"static/css/main.ce273286.css","sourcesContent":["body {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/index.css",".footer-navbar-text-center {\n width: 100%;\n text-align:center;\n}\n\n\n// WEBPACK FOOTER //\n// ./src/components/footer/Footer.css",".custom-image {\n width: 300px;\n height: 300px!important;\n}\n.ratings {\n padding-right: 10px;\n padding-left: 10px;\n color: #d17581;\n}\n.margin-left-sm {\n margin-left: 10px;\n}\n\n\n// WEBPACK FOOTER //\n// ./src/components/item/Item.css"],"sourceRoot":""} -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/HELP-US-OUT.txt: -------------------------------------------------------------------------------- 1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, 2 | Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, 3 | comprehensive icon sets or copy and paste your own. 4 | 5 | Please. Check it out. 6 | 7 | -Dave Gandy 8 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend/src/main/resources/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/font-awesome-4.7.0/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /storefrontend/src/main/resources/static/service-worker.js: -------------------------------------------------------------------------------- 1 | "use strict";function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}var precacheConfig=[["/index.html","8ab0756886cbec8a9e937504209f40f0"],["/static/css/main.ce273286.css","072a169920a0b22d616e1184deeea543"],["/static/js/main.301a64e3.js","a421913ffc02691630b7f16aad9eb442"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var n=new URL(e);return"/"===n.pathname.slice(-1)&&(n.pathname+=t),n.toString()},cleanResponse=function(e){if(!e.redirected)return Promise.resolve(e);return("body"in e?Promise.resolve(e.body):e.blob()).then(function(t){return new Response(t,{headers:e.headers,status:e.status,statusText:e.statusText})})},createCacheKey=function(e,t,n,r){var a=new URL(e);return r&&a.pathname.match(r)||(a.search+=(a.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(n)),a.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var n=new URL(t).pathname;return e.some(function(e){return n.match(e)})},stripIgnoredUrlParameters=function(e,t){var n=new URL(e);return n.hash="",n.search=n.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(e){return t.every(function(t){return!t.test(e[0])})}).map(function(e){return e.join("=")}).join("&"),n.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],n=e[1],r=new URL(t,self.location),a=createCacheKey(r,hashParamName,n,/\.\w{8}\./);return[r.toString(),a]}));self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(e){return setOfCachedUrls(e).then(function(t){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(n){if(!t.has(n)){var r=new Request(n,{credentials:"same-origin"});return fetch(r).then(function(t){if(!t.ok)throw new Error("Request for "+n+" returned a response with status "+t.status);return cleanResponse(t).then(function(t){return e.put(n,t)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var t=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(e){return e.keys().then(function(n){return Promise.all(n.map(function(n){if(!t.has(n.url))return e.delete(n)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(e){if("GET"===e.request.method){var t,n=stripIgnoredUrlParameters(e.request.url,ignoreUrlParametersMatching),r="index.html";(t=urlsToCacheKeys.has(n))||(n=addDirectoryIndex(n,r),t=urlsToCacheKeys.has(n));var a="/index.html";!t&&"navigate"===e.request.mode&&isPathWhitelisted(["^(?!\\/__).*"],e.request.url)&&(n=new URL(a,self.location).toString(),t=urlsToCacheKeys.has(n)),t&&e.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(n)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(t){return console.warn('Couldn\'t serve response for "%s" from cache: %O',e.request.url,t),fetch(e.request)}))}}); -------------------------------------------------------------------------------- /storefrontend/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | React App 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /storefrontend/src/test/java/com/storefrontend/StorefrontendApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.storefrontend; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class StorefrontendApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /storefrontend_proto/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "standard" 3 | }; -------------------------------------------------------------------------------- /storefrontend_proto/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | /public/bootstrap 24 | /public/font-awesome-4.7.0 -------------------------------------------------------------------------------- /storefrontend_proto/README.md: -------------------------------------------------------------------------------- 1 | # storefrontend_proto 2 | 3 | ## basic setup 4 | ``` 5 | create-react-app storefrontend_proto 6 | ``` 7 | 8 | ## other setup 9 | 10 | ``` 11 | npm install --save react-toastify 12 | ``` -------------------------------------------------------------------------------- /storefrontend_proto/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storefrontend_proto", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.2.0", 7 | "react-dom": "^16.2.0", 8 | "react-scripts": "1.0.17", 9 | "react-toastify": "^3.0.0", 10 | "superagent": "^3.8.1" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | }, 18 | "devDependencies": { 19 | "eslint-config-standard": "^10.2.1", 20 | "eslint-plugin-import": "^2.8.0", 21 | "eslint-plugin-node": "^5.2.1", 22 | "eslint-plugin-promise": "^3.6.0", 23 | "eslint-plugin-standard": "^3.0.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /storefrontend_proto/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developma/spring-microservice-for-e-commerce/2419be081b54c28cc01349f9a0d29ed99886bd01/storefrontend_proto/public/favicon.ico -------------------------------------------------------------------------------- /storefrontend_proto/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 24 | React App 25 | 26 | 27 | 30 |
31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /storefrontend_proto/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /storefrontend_proto/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /storefrontend_proto/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Header from './components/header/Header' 3 | import Footer from './components/footer/Footer' 4 | import ItemList from './components/item/ItemList' 5 | import {ToastContainer, toast} from 'react-toastify' 6 | 7 | export default class App extends Component { 8 | 9 | constructor (props) { 10 | super(props) 11 | this.state = { 12 | item: null 13 | } 14 | } 15 | 16 | onAddToCartCallback (e) { 17 | this.setState({ 18 | item: e 19 | }) 20 | // TODO Add codes to add clicked item to cart. 21 | toast.success("The " + e.name + " has added to cart successfully.", { 22 | position: toast.POSITION.TOP_CENTER 23 | }) 24 | } 25 | 26 | render () { 27 | return ( 28 |
29 |
30 | this.onAddToCartCallback(e)}/> 31 |
32 | 33 |
34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /storefrontend_proto/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /storefrontend_proto/src/components/cart/Cart.js: -------------------------------------------------------------------------------- 1 | export default class Cart { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /storefrontend_proto/src/components/cart/CartHolder.js: -------------------------------------------------------------------------------- 1 | export default class CartHolder { 2 | addCart (cart) { 3 | } 4 | } 5 | 6 | CartHolder.list = [] 7 | -------------------------------------------------------------------------------- /storefrontend_proto/src/components/footer/Footer.css: -------------------------------------------------------------------------------- 1 | .footer-navbar-text-center { 2 | width: 100%; 3 | text-align:center; 4 | } -------------------------------------------------------------------------------- /storefrontend_proto/src/components/footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import './Footer.css' 3 | 4 | export default class Footer extends Component { 5 | render () { 6 | return ( 7 |
8 |
9 |

10 | Checkout repository at 11 | github 12 |

13 |
14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /storefrontend_proto/src/components/header/Header.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import request from 'superagent' 3 | 4 | export default class Header extends Component { 5 | constructor (props) { 6 | super(props) 7 | this.state = { 8 | totalUnit: 0, 9 | totalFee: 0 10 | } 11 | this.itemList = [] 12 | } 13 | 14 | componentWillReceiveProps (nextProps) { 15 | const item = nextProps.item 16 | this.itemList.push(item) 17 | let totalFee = 0 18 | this.itemList.forEach(item => { 19 | totalFee = totalFee + item.price 20 | }) 21 | this.setState({ 22 | totalUnit: this.itemList.length, 23 | totalFee: totalFee 24 | }) 25 | } 26 | 27 | onCheckOutClick (e) { 28 | if (this.state.totalUnit === 0) { 29 | return alert('please add some items to cart.') 30 | } 31 | 32 | const orderedItem = [] 33 | this.itemList.forEach(item => { 34 | orderedItem.push({ 35 | registerId: 0, 36 | id: item.id, 37 | unit: 1, 38 | versionno: 0 39 | }) 40 | }) 41 | 42 | // This is dummy data for passing process of order. 43 | const order = { 44 | registerId: 0, 45 | orderedItem: orderedItem, 46 | senderName: 'TestTestTest', 47 | address: { 48 | registerId: 0, 49 | zipCode: '123-4567', 50 | location: 'TestLocTestLoc', 51 | receiverName: 'NameNameName' 52 | } 53 | } 54 | 55 | request.post('http://localhost:8210/shipping/order/') 56 | .set('Content-Type', 'application/json') 57 | .send(order) 58 | .end((err, res) => { 59 | if (err || !res.ok) { 60 | const errorInfo = JSON.parse(res.text) 61 | if (errorInfo) { 62 | window.alert(errorInfo.message) 63 | } else { 64 | window.alert('post error' + JSON.stringify(err)) 65 | console.log(JSON.stringify(err)) 66 | } 67 | } else if (res.text === 'success') { 68 | window.alert('your order has been completed.') 69 | console.log(JSON.stringify(res)) 70 | this.itemList.splice(0, this.itemList.length) 71 | this.setState({ 72 | totalUnit: 0, 73 | totalFee: 0 74 | }) 75 | } else { 76 | window.alert('an error occurs during ordering process.') 77 | } 78 | }) 79 | } 80 | 81 | render () { 82 | return ( 83 | 97 | ) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /storefrontend_proto/src/components/item/Item.css: -------------------------------------------------------------------------------- 1 | .custom-image { 2 | width: 300px; 3 | height: 300px!important; 4 | } 5 | .ratings { 6 | padding-right: 10px; 7 | padding-left: 10px; 8 | color: #d17581; 9 | } 10 | .margin-left-sm { 11 | margin-left: 10px; 12 | } -------------------------------------------------------------------------------- /storefrontend_proto/src/components/item/Item.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import './Item.css' 3 | 4 | export default class Item extends Component { 5 | constructor (props) { 6 | super(props) 7 | this.state = { 8 | unit: this.props.unit, 9 | id: this.props.id, 10 | price: this.props.price, 11 | name: this.props.name 12 | } 13 | } 14 | 15 | onAddToCartClick (e) { 16 | this.props.onAddToCartCallback(this.state) 17 | } 18 | 19 | render() { 20 | return ( 21 |
22 |
23 | 24 |
25 |

{this.props.price} Yen.

26 |

{this.props.name}

27 |

{this.props.description}

28 |
29 |
30 | {this.state.unit} left in stock 31 |

32 | 33 |

34 |
35 |
36 |
37 |
38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /storefrontend_proto/src/components/item/ItemList.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import Item from './Item' 3 | import request from 'superagent' 4 | 5 | export default class ItemList extends Component { 6 | constructor (props) { 7 | super(props) 8 | this.state = { 9 | list: [] 10 | } 11 | } 12 | 13 | componentDidMount () { 14 | request.get('http://localhost:8200/inventory/items/') 15 | .end((err, res) => { 16 | if (err || !res.ok) { 17 | window.alert('error') 18 | } else { 19 | const list = [] 20 | res.body.forEach((element) => { 21 | list.push(element) 22 | } 23 | ) 24 | this.setState({ 25 | list: list 26 | }) 27 | } 28 | } 29 | ) 30 | } 31 | 32 | onAddToCartCallback (e) { 33 | this.props.onAddToCartCallback(e) 34 | } 35 | 36 | render () { 37 | return ( 38 |
39 |
40 |
41 |
42 | { 43 | (() => { 44 | return this.state.list.map(e => ( 45 | this.onAddToCartCallback(e)} 46 | id={e.id} 47 | name={e.name} 48 | price={e.price} 49 | pict={e.pict} 50 | unit={e.unit} 51 | /> 52 | )) 53 | } 54 | )() 55 | } 56 |
57 |
58 |
59 |
60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /storefrontend_proto/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /storefrontend_proto/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /storefrontend_proto/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /storefrontend_proto/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | } else { 39 | // Is not local host. Just register service worker 40 | registerValidSW(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | --------------------------------------------------------------------------------