├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── com │ │ └── mediasoft │ │ └── product │ │ ├── ServiceProductApplication.java │ │ ├── client │ │ ├── CustomerClient.java │ │ ├── CustomerHystrixFallbackFactory.java │ │ └── ProductClient.java │ │ ├── controller │ │ ├── CustomerController.java │ │ ├── ErrorMessage.java │ │ └── ProductController.java │ │ ├── entity │ │ ├── Category.java │ │ ├── Customer.java │ │ ├── Invoice.java │ │ ├── InvoiceItem.java │ │ ├── Product.java │ │ └── Region.java │ │ ├── models │ │ ├── Category.java │ │ ├── Customer.java │ │ ├── Product.java │ │ └── Region.java │ │ ├── repository │ │ ├── CustomerRepository.java │ │ ├── InvoiceItemsRepository.java │ │ ├── InvoiceRepository.java │ │ └── ProductRepository.java │ │ └── service │ │ ├── CustomerService.java │ │ ├── ICustomerService.java │ │ ├── IProductService.java │ │ └── ProductService.java └── resources │ ├── application.properties │ └── data.sql └── test └── java └── com └── mediasoft └── product ├── ProductRepositoryMockTest.java ├── ProductServiceMockTest.java └── ServiceProductApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MICROSERVICE WITH SPRING BOOT AND SPRING CLOUD 2 | 3 | ## Description 4 | 5 | This repository is a Software of Application with JAVA. 6 | 7 | ## Installation 8 | 9 | Using Spring Boot, JPA, Eureka, Sleuth, Actuator, etc preferably. 10 | 11 | ## Software and Automatization 12 | 13 | Apply Gradle 6.7.1 14 | 15 | ## Tools 16 | 17 | Java version JDK-11 18 | 19 | ## IDE 20 | 21 | IntelliJ IDEA 22 | 23 | ## Database 24 | 25 | Using H2Databse, MySQL,etc 26 | 27 | ## Client Rest 28 | 29 | Postman, Insomnia, Talend API Tester,etc 30 | 31 | ![alt text](https://raw.githubusercontent.com/paulc4/microservices-demo/master/shopping-system.jpg) 32 | ## Usage 33 | 34 | ```html 35 | $ git clone https://github.com/DanielArturoAlejoAlvarez/Spring-Boot-Gradle-Spring-Cloud-Microservice.git 36 | [NAME APP] 37 | 38 | ``` 39 | 40 | Follow the following steps and you're good to go! Important: 41 | 42 | ![alt text](https://blog.jetbrains.com/wp-content/uploads/2017/11/idea-image2-1.gif) 43 | ## Coding 44 | 45 | ### Config 46 | ```java 47 | 48 | ``` 49 | 50 | ### Database 51 | ```java 52 | 53 | ``` 54 | 55 | ### Controllers 56 | ```java 57 | 58 | ``` 59 | 60 | ### Models 61 | ```java 62 | 63 | ``` 64 | 65 | ### Services 66 | ```java 67 | 68 | ``` 69 | 70 | ## Contributing 71 | 72 | Bug reports and pull requests are welcome on GitHub at https://github.com/DanielArturoAlejoAlvarez/Spring-Boot-Gradle-Spring-Cloud-Microservice. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 73 | 74 | ## License 75 | 76 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 77 | ```` -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.4.1' 3 | id 'io.spring.dependency-management' version '1.0.10.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.mediasoft' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '11' 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | maven { url 'https://repo.spring.io/milestone' } 20 | } 21 | 22 | ext { 23 | set('springCloudVersion', "2020.0.0-RC1") 24 | } 25 | 26 | dependencies { 27 | //implementation 'org.springframework.boot:spring-boot-starter-actuator' 28 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 29 | implementation 'org.springframework.boot:spring-boot-starter-validation' 30 | implementation 'org.springframework.boot:spring-boot-starter-web' 31 | //implementation 'org.springframework.cloud:spring-cloud-starter-config' 32 | //implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' 33 | //implementation 'org.springframework.cloud:spring-cloud-starter-sleuth' 34 | compileOnly 'org.projectlombok:lombok' 35 | runtimeOnly 'mysql:mysql-connector-java' 36 | annotationProcessor 'org.projectlombok:lombok' 37 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 38 | implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' 39 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix' 40 | //implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard' 41 | implementation (group: 'org.springframework.cloud', name: 'spring-cloud-netflix-hystrix-dashboard', version: '2.2.2.RELEASE') 42 | } 43 | 44 | dependencyManagement { 45 | imports { 46 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 47 | } 48 | } 49 | 50 | test { 51 | useJUnitPlatform() 52 | } 53 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanielArturoAlejoAlvarez/Spring-Boot-Gradle-Spring-Cloud-Microservice/a0c46aa56d5fe048bfe8b68195e98f3ebd98418a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'service-product' 2 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/ServiceProductApplication.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ServiceProductApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ServiceProductApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/client/CustomerClient.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.client; 2 | 3 | import com.mediasoft.product.models.Customer; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | 9 | @FeignClient(name = "customer-service", fallback = CustomerHystrixFallbackFactory.class) 10 | public interface CustomerClient { 11 | 12 | @GetMapping(name = "/customers/{id}") 13 | public ResponseEntity getCustomer(@PathVariable("id") Long id); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/client/CustomerHystrixFallbackFactory.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.client; 2 | 3 | import com.mediasoft.product.models.Customer; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class CustomerHystrixFallbackFactory implements CustomerClient { 9 | 10 | @Override 11 | public ResponseEntity getCustomer(Long id) { 12 | Customer customer = Customer.builder() 13 | .firstName("none") 14 | .lastName("none") 15 | .email("none") 16 | .photoUrl("none") 17 | .build(); 18 | return ResponseEntity.ok(customer); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/client/ProductClient.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.client; 2 | 3 | import com.mediasoft.product.models.Product; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | 11 | @FeignClient("product-service") 12 | @RequestMapping(value = "/'products") 13 | public interface ProductClient { 14 | 15 | @GetMapping(name = "/{id}") 16 | public ResponseEntity getProduct(@PathVariable("id") Long id); 17 | 18 | @GetMapping(name = "/{id}/stock") 19 | public ResponseEntity updStockProduct(@PathVariable("id") Long id, @RequestParam(name = "qty", required = true) Double qty); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/controller/CustomerController.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.controller; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.mediasoft.product.entity.Customer; 6 | import com.mediasoft.product.entity.Region; 7 | import com.mediasoft.product.service.ICustomerService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.validation.BindingResult; 13 | import org.springframework.web.bind.annotation.*; 14 | import org.springframework.web.server.ResponseStatusException; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.stream.Collectors; 21 | 22 | @RestController 23 | @Slf4j 24 | @RequestMapping(value = "/customers") 25 | public class CustomerController { 26 | @Autowired 27 | private ICustomerService service; 28 | 29 | @GetMapping 30 | public ResponseEntity> listCustomer(@RequestParam(name = "regionId", required = false) Long regionId) { 31 | List customers = new ArrayList<>(); 32 | if (null==regionId) { 33 | customers = service.listAllCustomer(); 34 | if (customers.isEmpty()) { 35 | return ResponseEntity.noContent().build(); 36 | } 37 | }else { 38 | Region region = new Region(); 39 | region.setId(regionId); 40 | customers = service.listCustomerByRegion(region); 41 | if (null==customers) { 42 | log.error("Customers with Region id {} not found.", regionId); 43 | return ResponseEntity.notFound().build(); 44 | } 45 | } 46 | return ResponseEntity.ok(customers); 47 | } 48 | 49 | @GetMapping(name = "/{id}") 50 | public ResponseEntity getCustomer(@PathVariable("id") Long id) { 51 | log.info("Fetching Customer with id {}", id); 52 | Customer customer = service.getCustomer(id); 53 | if (null==customer) { 54 | log.error("Customer with id {} not found.", id); 55 | return ResponseEntity.notFound().build(); 56 | } 57 | return ResponseEntity.ok(customer); 58 | } 59 | 60 | @PostMapping 61 | public ResponseEntity createCustomer(@RequestBody Customer customer, BindingResult result) { 62 | log.info("Creating Customer : {}", customer); 63 | if (result.hasErrors()) { 64 | throw new ResponseStatusException(HttpStatus.BAD_REQUEST, this.formatMessage(result)); 65 | } 66 | Customer customerDB = service.createCustomer(customer); 67 | return ResponseEntity.status(HttpStatus.CREATED).body(customerDB); 68 | } 69 | 70 | @PutMapping(name = "/{id}") 71 | public ResponseEntity updateCustomer(@PathVariable("id") Long id, @RequestBody Customer customer) { 72 | log.info("Updating Customer with id {}", id); 73 | Customer currentCustomer = service.getCustomer(id); 74 | if (null==currentCustomer) { 75 | log.error("Unable to update. Customer with id {} not found.", id); 76 | return ResponseEntity.notFound().build(); 77 | } 78 | customer.setId(id); 79 | currentCustomer = service.updateCustomer(customer); 80 | return ResponseEntity.ok(currentCustomer); 81 | } 82 | 83 | @DeleteMapping(name = "/{id}") 84 | public ResponseEntity deleteCustomer(Long id) { 85 | log.info("Fetching & Deleting Customer with id {}", id); 86 | Customer customer = service.getCustomer(id); 87 | if (null==customer) { 88 | log.error("Unable to delete. Customer with id {} not found.", id); 89 | return ResponseEntity.notFound().build(); 90 | } 91 | customer = service.deleteCustomer(customer); 92 | return ResponseEntity.ok(customer); 93 | } 94 | 95 | 96 | private String formatMessage( BindingResult result){ 97 | List> errors = result.getFieldErrors().stream() 98 | .map(err ->{ 99 | Map error = new HashMap<>(); 100 | error.put(err.getField(), err.getDefaultMessage()); 101 | return error; 102 | 103 | }).collect(Collectors.toList()); 104 | ErrorMessage errorMessage = ErrorMessage.builder() 105 | .code("01") 106 | .messages(errors).build(); 107 | ObjectMapper mapper = new ObjectMapper(); 108 | String jsonString=""; 109 | try { 110 | jsonString = mapper.writeValueAsString(errorMessage); 111 | } catch (JsonProcessingException e) { 112 | e.printStackTrace(); 113 | } 114 | return jsonString; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/controller/ErrorMessage.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.controller; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | @Getter @Setter @Builder 11 | public class ErrorMessage { 12 | private String code; 13 | private List> messages; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/controller/ProductController.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.controller; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.mediasoft.product.entity.Category; 6 | import com.mediasoft.product.entity.Product; 7 | import com.mediasoft.product.service.IProductService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.validation.BindingResult; 12 | import org.springframework.web.bind.annotation.*; 13 | import org.springframework.web.server.ResponseStatusException; 14 | 15 | import javax.validation.Valid; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.stream.Collectors; 21 | 22 | @RestController 23 | @RequestMapping(value = "/products") 24 | public class ProductController { 25 | 26 | @Autowired 27 | private IProductService service; 28 | 29 | /*@GetMapping 30 | public ResponseEntity> listProduct() { 31 | List products = service.listAllProduct(); 32 | if (products.isEmpty()) { 33 | return ResponseEntity.noContent().build(); 34 | } 35 | return ResponseEntity.ok(products); 36 | }*/ 37 | @GetMapping 38 | public ResponseEntity> listProduct(@RequestParam(name = "categoryId", required = false) Long categoryId) { 39 | List products = new ArrayList<>(); 40 | if (null==categoryId) { 41 | products = service.listAllProduct(); 42 | if (products.isEmpty()) { 43 | return ResponseEntity.noContent().build(); 44 | } 45 | }else { 46 | products = service.findByCategory(Category.builder().id(categoryId).build()); 47 | if (products.isEmpty()) { 48 | return ResponseEntity.notFound().build(); 49 | } 50 | } 51 | return ResponseEntity.ok(products); 52 | } 53 | 54 | @GetMapping(value = "/{id}") 55 | public ResponseEntity getProduct(@PathVariable("id") Long id) { 56 | Product product = service.getProduct(id); 57 | if (null==product) { 58 | return ResponseEntity.notFound().build(); 59 | } 60 | return ResponseEntity.ok(product); 61 | } 62 | 63 | @PostMapping 64 | public ResponseEntity createProduct(@Valid @RequestBody Product product, BindingResult result) { 65 | 66 | if (result.hasErrors()) { 67 | throw new ResponseStatusException(HttpStatus.BAD_REQUEST, this.formatMessage(result)); 68 | } 69 | 70 | Product newPro = service.createProduct(product); 71 | return ResponseEntity.status(HttpStatus.CREATED).body(newPro); 72 | } 73 | 74 | @PutMapping(value = "/{id}") 75 | public ResponseEntity updateProduct(@PathVariable("id") Long id, @RequestBody Product product) { 76 | product.setId(id); 77 | Product updPro = service.updateProduct(product); 78 | if (null==updPro) { 79 | return ResponseEntity.notFound().build(); 80 | } 81 | return ResponseEntity.status(HttpStatus.CREATED).body(updPro); 82 | } 83 | 84 | @DeleteMapping(value = "/{id}") 85 | public ResponseEntity deleteProduct(@PathVariable("id") Long id) { 86 | Product delPro = service.deleteProduct(id); 87 | if (null==delPro){ 88 | return ResponseEntity.notFound().build(); 89 | } 90 | return ResponseEntity.ok(delPro); 91 | } 92 | 93 | @GetMapping(value = "/{id}/stock") 94 | public ResponseEntity updStockProduct(@PathVariable("id") Long id, @RequestParam(name = "qty",required = true) Double qty) { 95 | Product product = service.updStock(id,qty); 96 | if (null==product){ 97 | return ResponseEntity.notFound().build(); 98 | } 99 | return ResponseEntity.ok(product); 100 | } 101 | 102 | private String formatMessage(BindingResult result) { 103 | //Get Object Error Message 104 | List> errors = result.getFieldErrors().stream() 105 | .map(err ->{ 106 | Map error = new HashMap<>(); 107 | error.put(err.getField(), err.getDefaultMessage()); 108 | return error; 109 | }).collect(Collectors.toList()); 110 | ErrorMessage errorMessage = ErrorMessage.builder() 111 | .code("01") 112 | .messages(errors) 113 | .build(); 114 | 115 | //Converter to JSON String 116 | ObjectMapper mapper = new ObjectMapper(); 117 | String jsonString = ""; 118 | try { 119 | jsonString = mapper.writeValueAsString(errorMessage); 120 | }catch(JsonProcessingException e) { 121 | e.printStackTrace(); 122 | } 123 | return jsonString; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/entity/Category.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Table(name = "tbl_categories") 12 | @Data 13 | @AllArgsConstructor @NoArgsConstructor @Builder 14 | public class Category { 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | private String name; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/entity/Customer.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Data; 5 | 6 | import javax.persistence.*; 7 | import javax.validation.constraints.Email; 8 | import javax.validation.constraints.NotEmpty; 9 | import javax.validation.constraints.NotNull; 10 | import javax.validation.constraints.Size; 11 | import java.io.Serializable; 12 | 13 | 14 | @Entity 15 | @Data 16 | @Table(name = "tbl_customers") 17 | public class Customer implements Serializable { 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Long id; 21 | 22 | @NotEmpty(message = "The document number field must not be empty") 23 | @Size(min = 8, max = 8, message = "The size of the document number is 8") 24 | @Column(name = "number_id", length = 8, nullable = false, unique = true) 25 | private String numberID; 26 | 27 | @NotEmpty(message = "This field must not be empty!") 28 | @Column(name = "first_name", nullable = false) 29 | private String firstName; 30 | 31 | @NotEmpty(message = "This field must not be empty!") 32 | @Column(name = "last_name", nullable = false) 33 | private String lastName; 34 | 35 | @NotEmpty(message = "This field must not be empty!") 36 | @Email(message = "This field does not contain a well formed email") 37 | @Column(unique = true, nullable = false) 38 | private String email; 39 | 40 | @Column(name = "photo_url", length = 512) 41 | private String photoURL; 42 | 43 | @NotNull(message = "This field must not be empty!") 44 | @ManyToOne(fetch = FetchType.LAZY) 45 | @JoinColumn(name = "region_id") 46 | @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 47 | private Region region; 48 | 49 | private String status; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/entity/Invoice.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.Data; 5 | import org.hibernate.annotations.Persister; 6 | 7 | import javax.persistence.*; 8 | import javax.validation.Valid; 9 | import java.util.ArrayList; 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | @Entity 14 | @Data 15 | @Table(name = "tbl_invoices") 16 | public class Invoice { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | 21 | @Column(name = "number_invoice") 22 | private String numberInvoice; 23 | 24 | private String description; 25 | 26 | @Column(name = "customer_id") 27 | private Long customerId; 28 | 29 | @Column(name = "created_at") 30 | @Temporal(TemporalType.DATE) 31 | private Date createdAt; 32 | 33 | @Valid 34 | @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 35 | @JoinColumn(name = "invoice_id") 36 | @JsonIgnoreProperties({"hibernateLazyInitializer","handler"}) 37 | private List items; 38 | 39 | private String status; 40 | 41 | @Transient 42 | private Customer customer; 43 | 44 | public Invoice() { 45 | items = new ArrayList<>(); 46 | } 47 | 48 | @PrePersist 49 | public void prePersist() { 50 | this.createdAt = new Date(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/entity/InvoiceItem.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.entity; 2 | 3 | import javax.persistence.*; 4 | import javax.validation.constraints.Positive; 5 | 6 | @Entity 7 | @Table(name = "tbl_invoice_items") 8 | public class InvoiceItem { 9 | @Id 10 | @GeneratedValue(strategy = GenerationType.IDENTITY) 11 | private Long id; 12 | 13 | @Positive(message = "The field must be positive!") 14 | private Double qty; 15 | 16 | private Double price; 17 | 18 | @Column(name = "product_id") 19 | private Long productId; 20 | 21 | @Transient 22 | private Double subTotal; 23 | 24 | @Transient 25 | private Product product; 26 | 27 | public Double getSubTotal() { 28 | if (this.price > 0 && this.qty > 0) { 29 | return this.price * this.qty; 30 | }else { 31 | return (double) 0; 32 | } 33 | } 34 | 35 | public InvoiceItem() { 36 | this.qty = (double) 0; 37 | this.price = (double) 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/entity/Product.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.NotEmpty; 11 | import javax.validation.constraints.NotNull; 12 | import javax.validation.constraints.Positive; 13 | import java.util.Date; 14 | 15 | @Entity 16 | @Table(name = "tbl_products") 17 | @Data 18 | @AllArgsConstructor @NoArgsConstructor @Builder 19 | public class Product { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | private Long id; 24 | 25 | @NotEmpty(message = "The name field cannot be empty!") 26 | @Column(nullable = false) 27 | private String name; 28 | 29 | private String description; 30 | 31 | @Positive(message = "The stock field must be greater than zero") 32 | private Double stock; 33 | 34 | @Column(name = "price", nullable = false) 35 | private Double price; 36 | 37 | @Column(name = "image", length = 512) 38 | private String image; 39 | 40 | private String status; 41 | 42 | @Column(name = "created_at") 43 | @Temporal(TemporalType.TIMESTAMP) 44 | private Date createdAt; 45 | 46 | @NotNull(message = "The field name must not be null or empty") 47 | @ManyToOne(fetch = FetchType.LAZY) 48 | @JoinColumn(name = "category_id") 49 | @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 50 | private Category category; 51 | 52 | @Column(name = "code", nullable = false, unique = true) 53 | private String code; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/entity/Region.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.entity; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import java.io.Serializable; 7 | 8 | @Entity 9 | @Data 10 | @Table(name = "tbl_regions") 11 | public class Region implements Serializable { 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) 14 | private Long id; 15 | 16 | private String name; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/models/Category.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.models; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Category { 7 | private Long id; 8 | private String name; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/models/Customer.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.models; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data @Builder 7 | public class Customer { 8 | private Long id; 9 | private String numberID; 10 | private String firstName; 11 | private String lastName; 12 | private String email; 13 | private String photoUrl; 14 | private Region region; 15 | private String status; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/models/Product.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.models; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Product { 7 | private Long id; 8 | private String name; 9 | private String description; 10 | private Double stock; 11 | private Double price; 12 | private String status; 13 | private Category category; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/models/Region.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.models; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Region { 7 | private Long id; 8 | private String name; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/repository/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.repository; 2 | 3 | import com.mediasoft.product.entity.Customer; 4 | import com.mediasoft.product.entity.Region; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface CustomerRepository extends JpaRepository { 12 | public Customer findByNumberID(String numberID); 13 | public List findByLastName(String lastName); 14 | public List findByRegion(Region region); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/repository/InvoiceItemsRepository.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.repository; 2 | 3 | import com.mediasoft.product.entity.InvoiceItem; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface InvoiceItemsRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/repository/InvoiceRepository.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.repository; 2 | 3 | import com.mediasoft.product.entity.Invoice; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface InvoiceRepository extends JpaRepository { 11 | 12 | public List findByCustomerId(Long customerId); 13 | public Invoice findByNumberInvoice(String numberInvoice); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/repository/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.repository; 2 | 3 | import com.mediasoft.product.entity.Category; 4 | import com.mediasoft.product.entity.Product; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface ProductRepository extends JpaRepository { 12 | 13 | public List findByCategory(Category category); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/service/CustomerService.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.service; 2 | 3 | import com.mediasoft.product.entity.Customer; 4 | import com.mediasoft.product.entity.Region; 5 | import com.mediasoft.product.repository.CustomerRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.List; 11 | 12 | @Slf4j 13 | @Service 14 | public class CustomerService implements ICustomerService{ 15 | @Autowired 16 | private CustomerRepository repo; 17 | 18 | @Override 19 | public List listAllCustomer() { 20 | return repo.findAll(); 21 | } 22 | 23 | @Override 24 | public List listCustomerByRegion(Region region) { 25 | return repo.findByRegion(region); 26 | } 27 | 28 | @Override 29 | public Customer createCustomer(Customer customer) { 30 | Customer customerDB = repo.findByNumberID(customer.getNumberID()); 31 | if (customerDB != null) { 32 | return customerDB; 33 | } 34 | customer.setStatus("CREATED"); 35 | customerDB = repo.save(customer); 36 | return customerDB; 37 | } 38 | 39 | @Override 40 | public Customer updateCustomer(Customer customer) { 41 | Customer customerDB = getCustomer(customer.getId()); 42 | if (null==customerDB) { 43 | return null; 44 | } 45 | customerDB.setFirstName(customer.getFirstName()); 46 | customerDB.setLastName(customer.getLastName()); 47 | customerDB.setEmail(customer.getEmail()); 48 | customerDB.setPhotoURL(customer.getPhotoURL()); 49 | return repo.save(customerDB); 50 | } 51 | 52 | @Override 53 | public Customer deleteCustomer(Customer customer) { 54 | Customer customerDB = getCustomer(customer.getId()); 55 | if (null==customerDB) { 56 | return null; 57 | } 58 | customer.setStatus("DELETED"); 59 | return repo.save(customer); 60 | } 61 | 62 | @Override 63 | public Customer getCustomer(Long id) { 64 | return repo.findById(id).orElse(null); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/service/ICustomerService.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.service; 2 | 3 | import com.mediasoft.product.entity.Customer; 4 | import com.mediasoft.product.entity.Region; 5 | 6 | import java.util.List; 7 | 8 | public interface ICustomerService { 9 | public List listAllCustomer(); 10 | public List listCustomerByRegion(Region region); 11 | 12 | public Customer createCustomer(Customer customer); 13 | public Customer updateCustomer(Customer customer); 14 | public Customer deleteCustomer(Customer customer); 15 | public Customer getCustomer(Long id); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/service/IProductService.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.service; 2 | 3 | import com.mediasoft.product.entity.Category; 4 | import com.mediasoft.product.entity.Product; 5 | 6 | import java.util.List; 7 | 8 | public interface IProductService { 9 | 10 | public List listAllProduct(); 11 | public Product getProduct(Long id); 12 | public Product createProduct(Product p); 13 | public Product updateProduct(Product p); 14 | public Product deleteProduct(Long id); 15 | public List findByCategory(Category c); 16 | public Product updStock(Long id, Double qty); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mediasoft/product/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product.service; 2 | 3 | import com.mediasoft.product.entity.Category; 4 | import com.mediasoft.product.entity.Product; 5 | import com.mediasoft.product.repository.ProductRepository; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class ProductService implements IProductService{ 16 | //@Autowired 17 | private final ProductRepository repo; 18 | 19 | @Override 20 | public List listAllProduct() { 21 | return repo.findAll(); 22 | } 23 | 24 | @Override 25 | public Product getProduct(Long id) { 26 | return repo.findById(id).orElse(null); 27 | } 28 | 29 | @Override 30 | public Product createProduct(Product p) { 31 | p.setStatus("CREATED"); 32 | p.setCreatedAt(new Date()); 33 | return repo.save(p); 34 | } 35 | 36 | @Override 37 | public Product updateProduct(Product p) { 38 | Product prodDB = getProduct(p.getId()); 39 | if (null==prodDB) { 40 | return null; 41 | } 42 | prodDB.setName(p.getName()); 43 | prodDB.setDescription(p.getDescription()); 44 | prodDB.setPrice(p.getPrice()); 45 | prodDB.setImage(p.getImage()); 46 | prodDB.setCategory(p.getCategory()); 47 | 48 | return repo.save(prodDB); 49 | } 50 | 51 | @Override 52 | public Product deleteProduct(Long id) { 53 | Product prodDB = getProduct(id); 54 | if (null==prodDB) { 55 | return null; 56 | } 57 | prodDB.setStatus("DELETED"); 58 | 59 | return repo.save(prodDB); 60 | } 61 | 62 | @Override 63 | public List findByCategory(Category c) { 64 | return repo.findByCategory(c); 65 | } 66 | 67 | @Override 68 | public Product updStock(Long id, Double qty) { 69 | Product prodDB = getProduct(id); 70 | if (null==prodDB) { 71 | return null; 72 | } 73 | Double stock = prodDB.getStock() - qty; 74 | prodDB.setStock(stock); 75 | return repo.save(prodDB); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mysql://127.0.0.1:3306/serviceprod_db?serverTimezone=UTC 2 | spring.datasource.username=root 3 | spring.datasource.password=Br1tney$2= 4 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 5 | 6 | spring.jpa.show-sql=true 7 | spring.jpa.properties.hibernate.format_sql=true 8 | 9 | spring.jpa.hibernate.ddl-auto=update 10 | 11 | server.port=8081 -------------------------------------------------------------------------------- /src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO tbl_categories (id,name) VALUES (1,"COMPUTERS"); 2 | INSERT INTO tbl_categories (id,name) VALUES (2,"TABLES"); 3 | INSERT INTO tbl_categories (id,name) VALUES (3,"SMARTPHONES"); 4 | 5 | INSERT INTO tbl_products (id,name,description,stock,price,image,status,created_at,category_id,code) VALUES (1,"iMac Pro","",2785.45,500,"",true,"","2020-12-25",1,"C-123"); 6 | INSERT INTO tbl_products (id,name,description,stock,price,image,status,created_at,category_id,code) VALUES (1,"iPad Air","",1875.65,1000,"",true,"","2020-12-26",2,"T-256"); 7 | INSERT INTO tbl_products (id,name,description,stock,price,image,status,created_at,category_id,code) VALUES (1,"iPhone X","",1395.25,5000,"",true,"","2020-12-27",3,"S-789"); -------------------------------------------------------------------------------- /src/test/java/com/mediasoft/product/ProductRepositoryMockTest.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product; 2 | 3 | import com.mediasoft.product.entity.Category; 4 | import com.mediasoft.product.entity.Product; 5 | import com.mediasoft.product.repository.ProductRepository; 6 | 7 | import static org.hamcrest.CoreMatchers.equalTo; 8 | import static org.hamcrest.CoreMatchers.is; 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 14 | 15 | import java.util.Date; 16 | import java.util.List; 17 | 18 | @DataJpaTest 19 | public class ProductRepositoryMockTest { 20 | @Autowired 21 | private ProductRepository repo; 22 | 23 | @Test 24 | public void whenFindByCategory_ThenReturnProductList() { 25 | Product prod1 = Product.builder() 26 | .code("C-255") 27 | .category(Category.builder().id(1L).build()) 28 | .name("iMac Pro") 29 | .description("") 30 | .stock(Double.parseDouble("500")) 31 | .price(Double.parseDouble("2789.25")) 32 | .image("https://store.storeimages.cdn-apple.com/4982/as-images.apple.com/is/imac-21-cto-hero-201903?wid=627&hei=522&fmt=jpeg&qlt=95&.v=1553120926388") 33 | .status("CREATED") 34 | .createdAt(new Date()) 35 | .build(); 36 | repo.save(prod1); 37 | 38 | List result = repo.findByCategory(prod1.getCategory()); 39 | assertThat(result.size(), is(equalTo(3))); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/mediasoft/product/ProductServiceMockTest.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product; 2 | 3 | import com.mediasoft.product.entity.Category; 4 | import com.mediasoft.product.entity.Product; 5 | import com.mediasoft.product.repository.ProductRepository; 6 | 7 | import static org.hamcrest.CoreMatchers.equalTo; 8 | import static org.hamcrest.CoreMatchers.is; 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | 11 | import com.mediasoft.product.service.IProductService; 12 | import com.mediasoft.product.service.ProductService; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.Test; 15 | import org.mockito.Mock; 16 | import org.mockito.Mockito; 17 | import org.mockito.MockitoAnnotations; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | 20 | import java.util.Optional; 21 | 22 | @SpringBootTest 23 | public class ProductServiceMockTest { 24 | @Mock 25 | private ProductRepository repo; 26 | 27 | private IProductService service; 28 | 29 | @BeforeEach 30 | public void setup() { 31 | MockitoAnnotations.initMocks(this); 32 | service = new ProductService(repo); 33 | Product computer = Product.builder() 34 | .id(1L) 35 | .code("C-741") 36 | .name("Notebook Pro") 37 | .description("") 38 | .stock(Double.parseDouble("500")) 39 | .price(Double.parseDouble("2785.25")) 40 | .image("") 41 | .category(Category.builder().id(1L).build()) 42 | .build(); 43 | 44 | Mockito.when(repo.findById(1L)).thenReturn(Optional.of(computer)); 45 | Mockito.when(repo.save(computer)).thenReturn(computer); 46 | } 47 | 48 | @Test 49 | public void whenValidGetID_ThenReturnProduct() { 50 | Product result = service.getProduct(1L); 51 | assertThat(result.getName(), is(equalTo("Notebook Pro"))); 52 | } 53 | 54 | public void whenValidUpdateStock_ThenReturnNewStock() { 55 | Product newStock = service.updStock(1L,Double.parseDouble("10")); 56 | assertThat(newStock.getStock(), is(equalTo(490))); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/mediasoft/product/ServiceProductApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.mediasoft.product; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ServiceProductApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------