diff --git a/build-all.sh b/build-all.sh index bafc2ee..d324f95 100755 --- a/build-all.sh +++ b/build-all.sh @@ -86,6 +86,7 @@ if [[ "$MODULE" == "module5" ]] then # ADD NEW MODULES HERE # (add new modules above the rest so you get quicker feedback if it fails) + build_gradle_module "spring-boot/exception-handling" build_maven_module "spring-boot/spring-boot-elasticsearch" build_gradle_module "spring-boot/spring-boot-mocking-modules" build_gradle_module "spring-boot/specification" diff --git a/spring-boot/exception-handling/README.md b/spring-boot/exception-handling/README.md new file mode 100644 index 0000000..8f01676 --- /dev/null +++ b/spring-boot/exception-handling/README.md @@ -0,0 +1,3 @@ +# Spring Exception handling tutorial +* The code examples in this project showcases various ways to handle exceptions in + a Spring Boot Application \ No newline at end of file diff --git a/spring-boot/exception-handling/build.gradle b/spring-boot/exception-handling/build.gradle new file mode 100644 index 0000000..20b5f53 --- /dev/null +++ b/spring-boot/exception-handling/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'org.springframework.boot' version '2.4.0' + id 'io.spring.dependency-management' version '1.0.10.RELEASE' + id 'java' +} + +group = 'io.reflectoring' +version = '0.0.1' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.apache.commons:commons-lang3' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.h2database:h2' + annotationProcessor 'org.projectlombok:lombok' + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } +} + +test { + useJUnitPlatform() +} diff --git a/spring-boot/exception-handling/gradle/wrapper/gradle-wrapper.jar b/spring-boot/exception-handling/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/spring-boot/exception-handling/gradle/wrapper/gradle-wrapper.jar differ diff --git a/spring-boot/exception-handling/gradle/wrapper/gradle-wrapper.properties b/spring-boot/exception-handling/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..900cfe2 --- /dev/null +++ b/spring-boot/exception-handling/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Nov 27 17:09:23 IST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/spring-boot/exception-handling/gradlew b/spring-boot/exception-handling/gradlew new file mode 100644 index 0000000..8f89047 --- /dev/null +++ b/spring-boot/exception-handling/gradlew @@ -0,0 +1,184 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ]; do + ls=$(ls -ld "$PRG") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' >/dev/null; then + PRG="$link" + else + PRG=$(dirname "$PRG")"/$link" + fi +done +SAVED="$(pwd)" +cd "$(dirname \"$PRG\")/" >/dev/null +APP_HOME="$(pwd -P)" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=$(basename "$0") + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn() { + echo "$*" +} + +die() { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$(uname)" in +CYGWIN*) + cygwin=true + ;; +Darwin*) + darwin=true + ;; +MINGW*) + msys=true + ;; +NONSTOP*) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ]; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then + MAX_FD_LIMIT=$(ulimit -H -n) + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ]; then + APP_HOME=$(cygpath --path --mixed "$APP_HOME") + CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") + + JAVACMD=$(cygpath --unix "$JAVACMD") + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) + SEP="" + for dir in $ROOTDIRSRAW; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ]; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@"; do + CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -) + CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition + eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") + else + eval $(echo args$i)="\"$arg\"" + fi + i=$(expr $i + 1) + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save() { + for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/spring-boot/exception-handling/gradlew.bat b/spring-boot/exception-handling/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/spring-boot/exception-handling/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/spring-boot/exception-handling/settings.gradle b/spring-boot/exception-handling/settings.gradle new file mode 100644 index 0000000..4865b0e --- /dev/null +++ b/spring-boot/exception-handling/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'exception' diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/ExceptionHandlingApplication.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/ExceptionHandlingApplication.java new file mode 100644 index 0000000..9728581 --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/ExceptionHandlingApplication.java @@ -0,0 +1,13 @@ +package io.reflectoring.exception; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ExceptionHandlingApplication { + + public static void main(String[] args) { + SpringApplication.run(ExceptionHandlingApplication.class, args); + } + +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/commons/I18Constants.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/commons/I18Constants.java new file mode 100644 index 0000000..20ce9b4 --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/commons/I18Constants.java @@ -0,0 +1,13 @@ +package io.reflectoring.exception.commons; + +import lombok.Getter; + +@Getter +public enum I18Constants { + NO_ITEM_FOUND("item.absent"); + + String key; + I18Constants(String key) { + this.key = key; + } +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/controller/ProductController.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/controller/ProductController.java new file mode 100644 index 0000000..4ace982 --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/controller/ProductController.java @@ -0,0 +1,30 @@ +package io.reflectoring.exception.controller; + +import io.reflectoring.exception.entity.Product; +import io.reflectoring.exception.model.ProductInput; +import io.reflectoring.exception.service.ProductService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@Slf4j(topic = "PRODUCT_CONTROLLER") +@RestController +@RequestMapping("/product") +@AllArgsConstructor +public class ProductController { + private final ProductService productService; + + @GetMapping("/{id}") + public Product getProduct(@PathVariable String id){ + return productService.getProduct(id); + } + + @PostMapping + public Product addProduct(@RequestBody @Valid ProductInput input){ + return productService.addProduct(input); + } + + +} \ No newline at end of file diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/entity/Category.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/entity/Category.java new file mode 100644 index 0000000..2260a65 --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/entity/Category.java @@ -0,0 +1,10 @@ +package io.reflectoring.exception.entity; + +public enum Category { + MOBILE, + TV_APPLIANCES, + MEN_FASHION, + WOMEN_FASHION, + BOOKS, + BEAUTY +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/entity/Product.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/entity/Product.java new file mode 100644 index 0000000..06a224c --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/entity/Product.java @@ -0,0 +1,41 @@ +package io.reflectoring.exception.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +public class Product { + @Id + private String id; + + private String name; + + private Double price; + + private LocalDateTime manufacturingDate; + + + private Double weight; + + @Embedded + private Dimension dimension; + + @Enumerated(EnumType.STRING) + private Category category; + + @Embeddable + @Getter + @Setter + public static class Dimension { + private Double height; + + private Double width; + } + + +} \ No newline at end of file diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/ErrorResponse.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/ErrorResponse.java new file mode 100644 index 0000000..8c06e8a --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/ErrorResponse.java @@ -0,0 +1,37 @@ +package io.reflectoring.exception.exception; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.springframework.validation.Errors; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Getter +@Setter +@RequiredArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ErrorResponse { + private final int status; + private final String message; + private String stackTrace; + private List errors; + + @Getter + @Setter + @RequiredArgsConstructor + private static class ValidationError { + private final String field; + private final String message; + } + + public void addValidationError(String field, String message){ + if(Objects.isNull(errors)){ + errors = new ArrayList<>(); + } + errors.add(new ValidationError(field, message)); + } +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/GlobalExceptionHandler.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..fb3a5aa --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/GlobalExceptionHandler.java @@ -0,0 +1,89 @@ +package io.reflectoring.exception.exception; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.util.Objects; + +@Slf4j(topic = "GLOBAL_EXCEPTION_HANDLER") +@RestControllerAdvice +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + + public static final String TRACE = "trace"; + + @Value("${reflectoring.trace:false}") + private boolean printStackTrace; + + @Override + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse(HttpStatus.UNPROCESSABLE_ENTITY.value(), "Validation error. Check 'errors' field for details."); + for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) { + errorResponse.addValidationError(fieldError.getField(), fieldError.getDefaultMessage()); + } + return ResponseEntity.unprocessableEntity().body(errorResponse); + } + + @ExceptionHandler(NoSuchElementFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ResponseEntity handleNoSuchElementFoundException(NoSuchElementFoundException itemNotFoundException, WebRequest request) { + log.error("Failed to find the requested element", itemNotFoundException); + return buildErrorResponse(itemNotFoundException, HttpStatus.NOT_FOUND, request); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity handleAllUncaughtException(Exception exception, WebRequest request) { + log.error("Unknown error occurred", exception); + return buildErrorResponse(exception, "Unknown error occurred", HttpStatus.INTERNAL_SERVER_ERROR, request); + } + + private ResponseEntity buildErrorResponse(Exception exception, + HttpStatus httpStatus, + WebRequest request) { + return buildErrorResponse(exception, exception.getMessage(), httpStatus, request); + } + + private ResponseEntity buildErrorResponse(Exception exception, + String message, + HttpStatus httpStatus, + WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse(httpStatus.value(), message); + if (printStackTrace && isTraceOn(request)) { + errorResponse.setStackTrace(ExceptionUtils.getStackTrace(exception)); + } + return ResponseEntity.status(httpStatus).body(errorResponse); + } + + private boolean isTraceOn(WebRequest request) { + String[] value = request.getParameterValues(TRACE); + return Objects.nonNull(value) + && value.length > 0 + && value[0].contentEquals("true"); + } + + @Override + public ResponseEntity handleExceptionInternal( + Exception ex, + Object body, + HttpHeaders headers, + HttpStatus status, + WebRequest request) { + + return buildErrorResponse(ex, status, request); + } +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/NoSuchElementFoundException.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/NoSuchElementFoundException.java new file mode 100644 index 0000000..ca9172e --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/exception/NoSuchElementFoundException.java @@ -0,0 +1,13 @@ +package io.reflectoring.exception.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +public class NoSuchElementFoundException extends RuntimeException { + + public NoSuchElementFoundException(String message){ + super(message); + } + + +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/model/ProductInput.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/model/ProductInput.java new file mode 100644 index 0000000..57e393f --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/model/ProductInput.java @@ -0,0 +1,24 @@ +package io.reflectoring.exception.model; + +import io.reflectoring.exception.entity.Category; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Getter +@Setter +public class ProductInput { + + @NotBlank + private String name; + + @NotNull + private Double price; + + private Double weight; + + private Category category; +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/model/Response.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/model/Response.java new file mode 100644 index 0000000..f100626 --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/model/Response.java @@ -0,0 +1,10 @@ +package io.reflectoring.exception.model; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Response { + private String message; +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/repository/ProductRepository.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/repository/ProductRepository.java new file mode 100644 index 0000000..82ecc71 --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/repository/ProductRepository.java @@ -0,0 +1,9 @@ +package io.reflectoring.exception.repository; + +import io.reflectoring.exception.entity.Product; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProductRepository extends JpaRepository { +} diff --git a/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/service/ProductService.java b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/service/ProductService.java new file mode 100644 index 0000000..304602c --- /dev/null +++ b/spring-boot/exception-handling/src/main/java/io/reflectoring/exception/service/ProductService.java @@ -0,0 +1,41 @@ +package io.reflectoring.exception.service; + +import io.reflectoring.exception.commons.I18Constants; +import io.reflectoring.exception.entity.Category; +import io.reflectoring.exception.entity.Product; +import io.reflectoring.exception.exception.NoSuchElementFoundException; +import io.reflectoring.exception.model.ProductInput; +import io.reflectoring.exception.repository.ProductRepository; +import lombok.AllArgsConstructor; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Service; + +import java.util.Locale; +import java.util.Objects; + +@Service +@AllArgsConstructor +public class ProductService { + private final ProductRepository repository; + private final MessageSource messageSource; + + public Product getProduct(String id) { + return repository.findById(id).orElseThrow(()-> + new NoSuchElementFoundException(getLocalMessage(I18Constants.NO_ITEM_FOUND.getKey(), id))); + } + + public Product addProduct(ProductInput productInput){ + Product product = new Product(); + product.setName(productInput.getName()); + product.setPrice(productInput.getPrice()); + product.setWeight(product.getWeight()); + product.setCategory(Objects.isNull(productInput.getCategory())? Category.BOOKS: productInput.getCategory()); + return repository.save(product); + } + + private String getLocalMessage(String key, String... params){ + return messageSource.getMessage(key, + params, + Locale.ENGLISH); + } +} diff --git a/spring-boot/exception-handling/src/main/resources/application.yaml b/spring-boot/exception-handling/src/main/resources/application.yaml new file mode 100644 index 0000000..77e94bd --- /dev/null +++ b/spring-boot/exception-handling/src/main/resources/application.yaml @@ -0,0 +1,21 @@ +server: + error: + include-message: always + include-binding-errors: always + include-stacktrace: on_trace_param + include-exception: true +reflectoring: + trace: true +spring: + jpa: + show-sql: true + generate-ddl: true + hibernate: + ddl-auto: create-drop + messages: + basename: lang/res + mvc: + throw-exception-if-no-handler-found: true + web: + resources: + add-mappings: false \ No newline at end of file diff --git a/spring-boot/exception-handling/src/main/resources/data.sql b/spring-boot/exception-handling/src/main/resources/data.sql new file mode 100644 index 0000000..3af326f --- /dev/null +++ b/spring-boot/exception-handling/src/main/resources/data.sql @@ -0,0 +1,29 @@ +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_1', 'one plus 8T', 100, now(), 2.2, 3.4, 5.4, 'MOBILE'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_2', 'Samsung 100 S', 200, now(), 5.6, 7.9, 4.3, 'MOBILE'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_3', 'Armani jacket size 32', 1100, now(), 1.3, 10.4, 29.5, 'MEN_FASHION'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_4', 'Zara purse', 500, now(), 3.7, 50.6, 70.6, 'WOMEN_FASHION'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_5', 'Sony Bravia ', 2000, now(), 25.5, 48.8, 25.9, 'TV_APPLIANCES'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_6', 'zara jacket green color', 1500, now(), 1.3, 10.4, 29.5, 'MEN_FASHION'); \ No newline at end of file diff --git a/spring-boot/exception-handling/src/main/resources/lang/res.properties b/spring-boot/exception-handling/src/main/resources/lang/res.properties new file mode 100644 index 0000000..457b203 --- /dev/null +++ b/spring-boot/exception-handling/src/main/resources/lang/res.properties @@ -0,0 +1 @@ +item.absent=Item with id {0} not found \ No newline at end of file diff --git a/spring-boot/exception-handling/src/test/java/io/reflectoring/exception/ExceptionHandlingApplicationTests.java b/spring-boot/exception-handling/src/test/java/io/reflectoring/exception/ExceptionHandlingApplicationTests.java new file mode 100644 index 0000000..eef3d60 --- /dev/null +++ b/spring-boot/exception-handling/src/test/java/io/reflectoring/exception/ExceptionHandlingApplicationTests.java @@ -0,0 +1,13 @@ +package io.reflectoring.exception; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ExceptionHandlingApplicationTests { + + @Test + void contextLoads() { + } + +}