├── .prettierignore ├── src ├── test │ ├── resources │ │ ├── i18n │ │ │ ├── messages_ko.properties │ │ │ └── messages_en.properties │ │ ├── config │ │ │ ├── bootstrap.yml │ │ │ └── application.yml │ │ └── logback.xml │ └── java │ │ └── com │ │ └── skcc │ │ └── book │ │ ├── repository │ │ ├── timezone │ │ │ ├── DateTimeWrapperRepository.java │ │ │ └── DateTimeWrapper.java │ │ └── BookRepositoryTest.java │ │ ├── config │ │ └── WebConfigurerTestController.java │ │ ├── domain │ │ ├── BookTest.java │ │ └── InStockBookTest.java │ │ ├── web │ │ └── rest │ │ │ ├── mapper │ │ │ ├── BookMapperTest.java │ │ │ └── InStockBookMapperTest.java │ │ │ ├── dto │ │ │ ├── BookDTOTest.java │ │ │ └── InStockBookDTOTest.java │ │ │ ├── errors │ │ │ └── ExceptionTranslatorTestController.java │ │ │ └── BookKafkaResourceIT.java │ │ ├── ArchTest.java │ │ └── security │ │ ├── SecurityUtilsUnitTest.java │ │ └── jwt │ │ └── TokenProviderTest.java └── main │ ├── java │ └── com │ │ └── skcc │ │ └── book │ │ ├── domain │ │ ├── package-info.java │ │ ├── enumeration │ │ │ ├── Location.java │ │ │ ├── Source.java │ │ │ ├── BookStatus.java │ │ │ └── Classification.java │ │ ├── UpdateBookEvent.java │ │ ├── event │ │ │ ├── StockChanged.java │ │ │ └── BookChanged.java │ │ ├── AbstractAuditingEntity.java │ │ ├── PersistentAuditEvent.java │ │ ├── InStockBook.java │ │ └── Book.java │ │ ├── service │ │ ├── package-info.java │ │ ├── InStockBookService.java │ │ ├── BookService.java │ │ └── impl │ │ │ └── InStockBookServiceImpl.java │ │ ├── config │ │ ├── audit │ │ │ ├── package-info.java │ │ │ └── AuditEventConverter.java │ │ ├── package-info.java │ │ ├── Constants.java │ │ ├── ApplicationProperties.java │ │ ├── LoggingAspectConfiguration.java │ │ ├── FeignConfiguration.java │ │ ├── DateTimeFormatConfiguration.java │ │ ├── CloudDatabaseConfiguration.java │ │ ├── LocaleConfiguration.java │ │ ├── KafkaProperties.java │ │ ├── JacksonConfiguration.java │ │ ├── AsyncConfiguration.java │ │ ├── DatabaseConfiguration.java │ │ ├── LoggingConfiguration.java │ │ ├── WebConfigurer.java │ │ ├── LiquibaseConfiguration.java │ │ └── SecurityConfiguration.java │ │ ├── web │ │ └── rest │ │ │ ├── package-info.java │ │ │ ├── vm │ │ │ └── package-info.java │ │ │ ├── errors │ │ │ ├── package-info.java │ │ │ ├── ErrorConstants.java │ │ │ ├── FieldErrorVM.java │ │ │ ├── BadRequestAlertException.java │ │ │ └── ExceptionTranslator.java │ │ │ ├── dto │ │ │ ├── BookInfoDTO.java │ │ │ ├── InStockBookDTO.java │ │ │ └── BookDTO.java │ │ │ ├── mapper │ │ │ ├── EntityMapper.java │ │ │ ├── BookMapper.java │ │ │ └── InStockBookMapper.java │ │ │ └── BookKafkaResource.java │ │ ├── repository │ │ ├── package-info.java │ │ ├── BookRepository.java │ │ └── InStockBookRepository.java │ │ ├── security │ │ ├── package-info.java │ │ ├── AuthoritiesConstants.java │ │ ├── SpringSecurityAuditorAware.java │ │ ├── jwt │ │ │ ├── JWTConfigurer.java │ │ │ ├── JWTFilter.java │ │ │ └── TokenProvider.java │ │ └── SecurityUtils.java │ │ ├── adaptor │ │ ├── BookProducer.java │ │ ├── BookProducerImpl.java │ │ └── BookConsumer.java │ │ ├── client │ │ └── UserFeignClientInterceptor.java │ │ ├── ApplicationWebXml.java │ │ ├── aop │ │ └── logging │ │ │ └── LoggingAspect.java │ │ └── BookApp.java │ ├── resources │ ├── config │ │ ├── tls │ │ │ └── keystore.p12 │ │ ├── bootstrap-prod.yml │ │ ├── liquibase │ │ │ ├── fake-data │ │ │ │ ├── in_stock_book.csv │ │ │ │ └── book.csv │ │ │ ├── master.xml │ │ │ └── changelog │ │ │ │ ├── 20200610085200_added_entity_InStockBook.xml │ │ │ │ ├── 00000000000000_initial_schema.xml │ │ │ │ └── 20200506084946_added_entity_Book.xml │ │ ├── bootstrap.yml │ │ └── application-tls.yml │ ├── .h2.server.properties │ ├── banner.txt │ ├── i18n │ │ ├── messages_en.properties │ │ ├── messages.properties │ │ └── messages_ko.properties │ ├── templates │ │ └── error.html │ ├── logback-spring.xml │ └── static │ │ └── index.html │ ├── docker │ ├── sonar.yml │ ├── hazelcast-management-center.yml │ ├── grafana │ │ └── provisioning │ │ │ ├── dashboards │ │ │ └── dashboard.yml │ │ │ └── datasources │ │ │ └── datasource.yml │ ├── mariadb.yml │ ├── central-server-config │ │ ├── README.md │ │ ├── localhost-config │ │ │ └── application.yml │ │ └── docker-config │ │ │ └── application.yml │ ├── kafka.yml │ ├── monitoring.yml │ ├── jhipster-registry.yml │ ├── app.yml │ └── prometheus │ │ └── prometheus.yml │ └── jib │ └── entrypoint.sh ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── .prettierrc ├── package.json ├── .editorconfig ├── checkstyle.xml ├── jhipster-jdl-2.jh ├── .jhipster ├── InStockBook.json └── Book.json ├── .yo-rc.json ├── sonar-project.properties ├── .gitignore ├── .gitattributes └── README.md /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target 3 | package-lock.json 4 | .git 5 | -------------------------------------------------------------------------------- /src/test/resources/i18n/messages_ko.properties: -------------------------------------------------------------------------------- 1 | email.test.title=\uc778\uc99d 2 | -------------------------------------------------------------------------------- /src/test/resources/config/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CNAPS-MSA/book/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * JPA domain objects. 3 | */ 4 | package com.skcc.book.domain; 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/service/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Service layer beans. 3 | */ 4 | package com.skcc.book.service; 5 | -------------------------------------------------------------------------------- /src/main/resources/config/tls/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CNAPS-MSA/book/HEAD/src/main/resources/config/tls/keystore.p12 -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/audit/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Audit specific code. 3 | */ 4 | package com.skcc.book.config.audit; 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Spring MVC REST controllers. 3 | */ 4 | package com.skcc.book.web.rest; 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Spring Framework configuration files. 3 | */ 4 | package com.skcc.book.config; 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/repository/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Spring Data JPA repositories. 3 | */ 4 | package com.skcc.book.repository; 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Spring Security configuration. 3 | */ 4 | package com.skcc.book.security; 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/vm/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * View Models used by Spring MVC REST controllers. 3 | */ 4 | package com.skcc.book.web.rest.vm; 5 | -------------------------------------------------------------------------------- /src/main/docker/sonar.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | book-sonar: 4 | image: sonarqube:8.2-community 5 | ports: 6 | - 9001:9000 7 | - 9092:9092 8 | -------------------------------------------------------------------------------- /src/main/resources/.h2.server.properties: -------------------------------------------------------------------------------- 1 | #H2 Server Properties 2 | 0=JHipster H2 (Memory)|org.h2.Driver|jdbc\:h2\:mem\:book|book 3 | webAllowOthers=true 4 | webPort=8082 5 | webSSL=false 6 | -------------------------------------------------------------------------------- /src/main/docker/hazelcast-management-center.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | book-hazelcast-management-center: 4 | image: hazelcast/management-center:3.12.8 5 | ports: 6 | - 8180:8080 7 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/enumeration/Location.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain.enumeration; 2 | 3 | /** 4 | * The Location enumeration. 5 | */ 6 | public enum Location { 7 | JEONGJA, PANGYO 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/enumeration/Source.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain.enumeration; 2 | 3 | /** 4 | * The Source enumeration. 5 | */ 6 | public enum Source { 7 | Donated, Purchased 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/enumeration/BookStatus.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain.enumeration; 2 | 3 | /** 4 | * The BookStatus enumeration. 5 | */ 6 | public enum BookStatus { 7 | AVAILABLE, UNAVAILABLE 8 | } 9 | -------------------------------------------------------------------------------- /src/test/resources/i18n/messages_en.properties: -------------------------------------------------------------------------------- 1 | email.test.title=test title 2 | # Value used for English locale unit test in MailServiceIT 3 | # as this file is loaded instead of real file 4 | email.activation.title=book account activation 5 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/errors/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Specific errors used with Zalando's "problem-spring-web" library. 3 | * 4 | * More information on https://github.com/zalando/problem-spring-web 5 | */ 6 | package com.skcc.book.web.rest.errors; 7 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/Constants.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | /** 4 | * Application constants. 5 | */ 6 | public final class Constants { 7 | 8 | public static final String SYSTEM_ACCOUNT = "system"; 9 | 10 | private Constants() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/jib/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP} 4 | exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "com.skcc.book.BookApp" "$@" 5 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/UpdateBookEvent.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class UpdateBookEvent { 9 | 10 | private Long bookId; 11 | private String bookStatus; 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/docker/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Prometheus' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | editable: true 10 | options: 11 | path: /etc/grafana/provisioning/dashboards 12 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/event/StockChanged.java: -------------------------------------------------------------------------------- 1 | 2 | package com.skcc.book.domain.event; 3 | 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | public class StockChanged { 10 | 11 | private Long bookId; 12 | private String bookStatus; 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | # Prettier configuration 2 | 3 | printWidth: 140 4 | singleQuote: true 5 | tabWidth: 2 6 | useTabs: false 7 | 8 | # js and ts rules: 9 | arrowParens: avoid 10 | 11 | # jsx and tsx rules: 12 | jsxBracketSameLine: false 13 | 14 | # java rules: 15 | overrides: 16 | - files: "*.java" 17 | options: 18 | tabWidth: 4 19 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/enumeration/Classification.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain.enumeration; 2 | 3 | /** 4 | * The Classification enumeration. 5 | */ 6 | public enum Classification { 7 | Arts, Photography, Biographies, BusinessMoney, Children, ComputerTechnology, History, Medical, Travel, Romance, Science, Math, SelfHelp 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "book", 3 | "version": "0.0.0", 4 | "description": "Description for book", 5 | "private": true, 6 | "license": "UNLICENSED", 7 | "cacheDirectories": [ 8 | "node_modules" 9 | ], 10 | "devDependencies": { 11 | "generator-jhipster": "6.8.0", 12 | "@openapitools/openapi-generator-cli": "1.0.10-4.2.3" 13 | }, 14 | "engines": { 15 | "node": ">=8.9.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/repository/BookRepository.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.repository; 2 | 3 | import com.skcc.book.domain.Book; 4 | 5 | import org.springframework.data.jpa.repository.*; 6 | import org.springframework.stereotype.Repository; 7 | 8 | /** 9 | * Spring Data repository for the Book entity. 10 | */ 11 | @SuppressWarnings("unused") 12 | @Repository 13 | public interface BookRepository extends JpaRepository { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/dto/BookInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.io.Serializable; 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class BookInfoDTO implements Serializable { 14 | private Long id; 15 | private String title; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/repository/timezone/DateTimeWrapperRepository.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.repository.timezone; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | /** 7 | * Spring Data JPA repository for the {@link DateTimeWrapper} entity. 8 | */ 9 | @Repository 10 | public interface DateTimeWrapperRepository extends JpaRepository { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/AuthoritiesConstants.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security; 2 | 3 | /** 4 | * Constants for Spring Security authorities. 5 | */ 6 | public final class AuthoritiesConstants { 7 | 8 | public static final String ADMIN = "ROLE_ADMIN"; 9 | 10 | public static final String USER = "ROLE_USER"; 11 | 12 | public static final String ANONYMOUS = "ROLE_ANONYMOUS"; 13 | 14 | private AuthoritiesConstants() { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/docker/mariadb.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | book-mariadb: 4 | image: mariadb:10.4.12 5 | # volumes: 6 | # - ~/volumes/jhipster/book/mysql/:/var/lib/mysql/ 7 | environment: 8 | - MYSQL_USER=root 9 | - MYSQL_ALLOW_EMPTY_PASSWORD=yes 10 | - MYSQL_DATABASE=book 11 | ports: 12 | - 3306:3306 13 | command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8mb4 --explicit_defaults_for_timestamp 14 | -------------------------------------------------------------------------------- /src/main/docker/central-server-config/README.md: -------------------------------------------------------------------------------- 1 | # Central configuration sources details 2 | 3 | The JHipster-Registry will use the following directories as its configuration source : 4 | 5 | - localhost-config : when running the registry in docker with the jhipster-registry.yml docker-compose file 6 | - docker-config : when running the registry and the app both in docker with the app.yml docker-compose file 7 | 8 | For more info, refer to https://www.jhipster.tech/jhipster-registry/#spring-cloud-config 9 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/config/WebConfigurerTestController.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class WebConfigurerTestController { 8 | 9 | @GetMapping("/api/test-cors") 10 | public void testCorsOnApiPath() { 11 | } 12 | 13 | @GetMapping("/test/test-cors") 14 | public void testCorsOnOtherPath() { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/mapper/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.mapper; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Contract for a generic dto to entity mapper. 7 | * 8 | * @param - DTO type parameter. 9 | * @param - Entity type parameter. 10 | */ 11 | 12 | public interface EntityMapper { 13 | 14 | E toEntity(D dto); 15 | 16 | D toDto(E entity); 17 | 18 | List toEntity(List dtoList); 19 | 20 | List toDto(List entityList); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * Properties specific to Book. 7 | *

8 | * Properties are configured in the {@code application.yml} file. 9 | * See {@link io.github.jhipster.config.JHipsterProperties} for a good example. 10 | */ 11 | @ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) 12 | public class ApplicationProperties { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/docker/central-server-config/localhost-config/application.yml: -------------------------------------------------------------------------------- 1 | # Common configuration shared between all applications 2 | configserver: 3 | name: Docker JHipster Registry 4 | status: Connected to the JHipster Registry running in Docker 5 | 6 | jhipster: 7 | security: 8 | authentication: 9 | jwt: 10 | secret: my-secret-key-which-should-be-changed-in-production-and-be-base64-encoded 11 | 12 | eureka: 13 | client: 14 | service-url: 15 | defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/ 16 | -------------------------------------------------------------------------------- /src/main/docker/central-server-config/docker-config/application.yml: -------------------------------------------------------------------------------- 1 | # Common configuration shared between all applications 2 | configserver: 3 | name: Docker JHipster Registry 4 | status: Connected to the JHipster Registry running in Docker 5 | 6 | jhipster: 7 | security: 8 | authentication: 9 | jwt: 10 | secret: my-secret-key-which-should-be-changed-in-production-and-be-base64-encoded 11 | 12 | eureka: 13 | client: 14 | service-url: 15 | defaultZone: http://admin:${jhipster.registry.password}@jhipster-registry:8761/eureka/ 16 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/adaptor/BookProducer.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.adaptor; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.skcc.book.domain.event.BookChanged; 5 | 6 | import java.util.concurrent.ExecutionException; 7 | 8 | public interface BookProducer { 9 | void sendBookCreateEvent(BookChanged bookChanged) throws ExecutionException, InterruptedException, JsonProcessingException; 10 | void sendBookDeleteEvent(BookChanged bookDeleteEvent) throws ExecutionException, InterruptedException, JsonProcessingException; 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # We recommend you to keep these unchanged 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | # Change these settings to your own preference 16 | indent_style = space 17 | indent_size = 4 18 | 19 | [*.{ts,tsx,js,jsx,json,css,scss,yml}] 20 | indent_size = 2 21 | 22 | [*.md] 23 | trim_trailing_whitespace = false 24 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/LoggingAspectConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import com.skcc.book.aop.logging.LoggingAspect; 4 | 5 | import io.github.jhipster.config.JHipsterConstants; 6 | 7 | import org.springframework.context.annotation.*; 8 | import org.springframework.core.env.Environment; 9 | 10 | @Configuration 11 | @EnableAspectJAutoProxy 12 | public class LoggingAspectConfiguration { 13 | 14 | @Bean 15 | @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) 16 | public LoggingAspect loggingAspect(Environment env) { 17 | return new LoggingAspect(env); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/repository/InStockBookRepository.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.repository; 2 | 3 | import com.skcc.book.domain.InStockBook; 4 | 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.jpa.repository.*; 8 | import org.springframework.stereotype.Repository; 9 | 10 | /** 11 | * Spring Data repository for the InStockBook entity. 12 | */ 13 | @SuppressWarnings("unused") 14 | @Repository 15 | public interface InStockBookRepository extends JpaRepository { 16 | Page findByTitleContaining(String title, Pageable pageable); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/mapper/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.mapper; 2 | 3 | 4 | import com.skcc.book.domain.*; 5 | import com.skcc.book.web.rest.dto.BookDTO; 6 | 7 | import org.mapstruct.*; 8 | 9 | /** 10 | * Mapper for the entity {@link Book} and its DTO {@link BookDTO}. 11 | */ 12 | @Mapper(componentModel = "spring", uses = {}) 13 | public interface BookMapper extends EntityMapper { 14 | 15 | 16 | 17 | default Book fromId(Long id) { 18 | if (id == null) { 19 | return null; 20 | } 21 | Book book = new Book(); 22 | book.setId(id); 23 | return book; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/SpringSecurityAuditorAware.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security; 2 | 3 | import com.skcc.book.config.Constants; 4 | 5 | import java.util.Optional; 6 | 7 | import org.springframework.data.domain.AuditorAware; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * Implementation of {@link AuditorAware} based on Spring Security. 12 | */ 13 | @Component 14 | public class SpringSecurityAuditorAware implements AuditorAware { 15 | 16 | @Override 17 | public Optional getCurrentAuditor() { 18 | return Optional.of(SecurityUtils.getCurrentUserLogin().orElse(Constants.SYSTEM_ACCOUNT)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/docker/kafka.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | zookeeper: 4 | image: confluentinc/cp-zookeeper:5.4.0 5 | environment: 6 | ZOOKEEPER_CLIENT_PORT: 2181 7 | ZOOKEEPER_TICK_TIME: 2000 8 | kafka: 9 | image: confluentinc/cp-kafka:5.4.0 10 | ports: 11 | - 9092:9092 12 | environment: 13 | KAFKA_BROKER_ID: 1 14 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 15 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 16 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 17 | KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT 18 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 19 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/event/BookChanged.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain.event; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class BookChanged { 13 | 14 | private String title; 15 | 16 | private String description; 17 | 18 | private String author; 19 | 20 | private String publicationDate; 21 | 22 | private String classification; 23 | 24 | private Boolean rented; 25 | 26 | private String eventType; 27 | 28 | private Long rentCnt; 29 | 30 | private Long bookId; 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/errors/ErrorConstants.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.errors; 2 | 3 | import java.net.URI; 4 | 5 | public final class ErrorConstants { 6 | 7 | public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; 8 | public static final String ERR_VALIDATION = "error.validation"; 9 | public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem"; 10 | public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message"); 11 | public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation"); 12 | 13 | private ErrorConstants() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/client/UserFeignClientInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.client; 2 | 3 | import com.skcc.book.security.SecurityUtils; 4 | import feign.RequestInterceptor; 5 | import feign.RequestTemplate; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class UserFeignClientInterceptor implements RequestInterceptor { 10 | private static final String AUTHORIZATION_HEADER = "Authorization"; 11 | private static final String BEARER = "Bearer"; 12 | 13 | @Override 14 | public void apply(RequestTemplate template) { 15 | SecurityUtils.getCurrentUserJWT() 16 | .ifPresent(s -> template.header(AUTHORIZATION_HEADER,String.format("%s %s", BEARER, s))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/mapper/InStockBookMapper.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.mapper; 2 | 3 | 4 | import com.skcc.book.domain.*; 5 | import com.skcc.book.web.rest.dto.InStockBookDTO; 6 | 7 | import org.mapstruct.*; 8 | 9 | /** 10 | * Mapper for the entity {@link InStockBook} and its DTO {@link InStockBookDTO}. 11 | */ 12 | @Mapper(componentModel = "spring", uses = {}) 13 | public interface InStockBookMapper extends EntityMapper { 14 | 15 | 16 | 17 | default InStockBook fromId(Long id) { 18 | if (id == null) { 19 | return null; 20 | } 21 | InStockBook inStockBook = new InStockBook(); 22 | inStockBook.setId(id); 23 | return inStockBook; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/domain/BookTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | import com.skcc.book.web.rest.TestUtil; 6 | 7 | public class BookTest { 8 | 9 | @Test 10 | public void equalsVerifier() throws Exception { 11 | TestUtil.equalsVerifier(Book.class); 12 | Book book1 = new Book(); 13 | book1.setId(1L); 14 | Book book2 = new Book(); 15 | book2.setId(book1.getId()); 16 | assertThat(book1).isEqualTo(book2); 17 | book2.setId(2L); 18 | assertThat(book1).isNotEqualTo(book2); 19 | book1.setId(null); 20 | assertThat(book1).isNotEqualTo(book2); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/web/rest/mapper/BookMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.mapper; 2 | 3 | import com.skcc.book.web.rest.mapper.BookMapperImpl; 4 | import com.skcc.book.web.rest.mapper.BookMapper; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class BookMapperTest { 10 | 11 | private BookMapper bookMapper; 12 | 13 | @BeforeEach 14 | public void setUp() { 15 | bookMapper = new BookMapperImpl(); 16 | } 17 | 18 | @Test 19 | public void testEntityFromId() { 20 | Long id = 1L; 21 | assertThat(bookMapper.fromId(id).getId()).isEqualTo(id); 22 | assertThat(bookMapper.fromId(null)).isNull(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/FeignConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import org.springframework.cloud.openfeign.EnableFeignClients; 4 | import org.springframework.cloud.openfeign.FeignClientsConfiguration; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Import; 8 | 9 | @Configuration 10 | @EnableFeignClients(basePackages = "com.skcc.book") 11 | @Import(FeignClientsConfiguration.class) 12 | public class FeignConfiguration { 13 | 14 | /** 15 | * Set the Feign specific log level to log client REST requests. 16 | */ 17 | @Bean 18 | feign.Logger.Level feignLoggerLevel() { 19 | return feign.Logger.Level.BASIC; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/errors/FieldErrorVM.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.errors; 2 | 3 | import java.io.Serializable; 4 | 5 | public class FieldErrorVM implements Serializable { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | private final String objectName; 10 | 11 | private final String field; 12 | 13 | private final String message; 14 | 15 | public FieldErrorVM(String dto, String field, String message) { 16 | this.objectName = dto; 17 | this.field = field; 18 | this.message = message; 19 | } 20 | 21 | public String getObjectName() { 22 | return objectName; 23 | } 24 | 25 | public String getField() { 26 | return field; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/DateTimeFormatConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.format.FormatterRegistry; 5 | import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | /** 9 | * Configure the converters to use the ISO format for dates by default. 10 | */ 11 | @Configuration 12 | public class DateTimeFormatConfiguration implements WebMvcConfigurer { 13 | 14 | @Override 15 | public void addFormatters(FormatterRegistry registry) { 16 | DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); 17 | registrar.setUseIsoFormat(true); 18 | registrar.registerFormatters(registry); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/web/rest/mapper/InStockBookMapperTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.mapper; 2 | 3 | import com.skcc.book.web.rest.mapper.InStockBookMapperImpl; 4 | import com.skcc.book.web.rest.mapper.InStockBookMapper; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | public class InStockBookMapperTest { 10 | 11 | private InStockBookMapper inStockBookMapper; 12 | 13 | @BeforeEach 14 | public void setUp() { 15 | inStockBookMapper = new InStockBookMapperImpl(); 16 | } 17 | 18 | @Test 19 | public void testEntityFromId() { 20 | Long id = 1L; 21 | assertThat(inStockBookMapper.fromId(id).getId()).isEqualTo(id); 22 | assertThat(inStockBookMapper.fromId(null)).isNull(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗ 3 | ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ 4 | ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ 5 | ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ 6 | ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ 7 | ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ 8 | 9 | ${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: 10 | :: https://www.jhipster.tech ::${AnsiColor.DEFAULT} 11 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_en.properties: -------------------------------------------------------------------------------- 1 | # Error page 2 | error.title=Your request cannot be processed 3 | error.subtitle=Sorry, an error has occurred. 4 | error.status=Status: 5 | error.message=Message: 6 | 7 | # Activation email 8 | email.activation.title=book account activation 9 | email.activation.greeting=Dear {0} 10 | email.activation.text1=Your book account has been created, please click on the URL below to activate it: 11 | email.activation.text2=Regards, 12 | email.signature=book Team. 13 | 14 | # Creation email 15 | email.creation.text1=Your book account has been created, please click on the URL below to access it: 16 | 17 | # Reset email 18 | email.reset.title=book password reset 19 | email.reset.greeting=Dear {0} 20 | email.reset.text1=For your book account a password reset was requested, please click on the URL below to reset it: 21 | email.reset.text2=Regards, 22 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages.properties: -------------------------------------------------------------------------------- 1 | # Error page 2 | error.title=Your request cannot be processed 3 | error.subtitle=Sorry, an error has occurred. 4 | error.status=Status: 5 | error.message=Message: 6 | 7 | # Activation email 8 | email.activation.title=book account activation is required 9 | email.activation.greeting=Dear {0} 10 | email.activation.text1=Your book account has been created, please click on the URL below to activate it: 11 | email.activation.text2=Regards, 12 | email.signature=book Team. 13 | 14 | # Creation email 15 | email.creation.text1=Your book account has been created, please click on the URL below to access it: 16 | 17 | # Reset email 18 | email.reset.title=book password reset 19 | email.reset.greeting=Dear {0} 20 | email.reset.text1=For your book account a password reset was requested, please click on the URL below to reset it: 21 | email.reset.text2=Regards, 22 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/domain/InStockBookTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | import com.skcc.book.web.rest.TestUtil; 6 | 7 | public class InStockBookTest { 8 | 9 | @Test 10 | public void equalsVerifier() throws Exception { 11 | TestUtil.equalsVerifier(InStockBook.class); 12 | InStockBook inStockBook1 = new InStockBook(); 13 | inStockBook1.setId(1L); 14 | InStockBook inStockBook2 = new InStockBook(); 15 | inStockBook2.setId(inStockBook1.getId()); 16 | assertThat(inStockBook1).isEqualTo(inStockBook2); 17 | inStockBook2.setId(2L); 18 | assertThat(inStockBook1).isNotEqualTo(inStockBook2); 19 | inStockBook1.setId(null); 20 | assertThat(inStockBook1).isNotEqualTo(inStockBook2); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/ApplicationWebXml.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book; 2 | 3 | import io.github.jhipster.config.DefaultProfileUtil; 4 | 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | 8 | /** 9 | * This is a helper Java class that provides an alternative to creating a {@code web.xml}. 10 | * This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc. 11 | */ 12 | public class ApplicationWebXml extends SpringBootServletInitializer { 13 | 14 | @Override 15 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 16 | // set a default to use when no profile is configured. 17 | DefaultProfileUtil.addDefaultProfile(application.application()); 18 | return application.sources(BookApp.class); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/web/rest/dto/BookDTOTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.dto; 2 | 3 | import com.skcc.book.web.rest.dto.BookDTO; 4 | import org.junit.jupiter.api.Test; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import com.skcc.book.web.rest.TestUtil; 7 | 8 | public class BookDTOTest { 9 | 10 | @Test 11 | public void dtoEqualsVerifier() throws Exception { 12 | TestUtil.equalsVerifier(BookDTO.class); 13 | BookDTO bookDTO1 = new BookDTO(); 14 | bookDTO1.setId(1L); 15 | BookDTO bookDTO2 = new BookDTO(); 16 | assertThat(bookDTO1).isNotEqualTo(bookDTO2); 17 | bookDTO2.setId(bookDTO1.getId()); 18 | assertThat(bookDTO1).isEqualTo(bookDTO2); 19 | bookDTO2.setId(2L); 20 | assertThat(bookDTO1).isNotEqualTo(bookDTO2); 21 | bookDTO1.setId(null); 22 | assertThat(bookDTO1).isNotEqualTo(bookDTO2); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/config/bootstrap-prod.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Spring Cloud Config bootstrap configuration for the "prod" profile 3 | # =================================================================== 4 | 5 | spring: 6 | cloud: 7 | config: 8 | fail-fast: true 9 | retry: 10 | initial-interval: 1000 11 | max-interval: 2000 12 | max-attempts: 100 13 | uri: http://admin:${jhipster.registry.password}@localhost:8761/config 14 | # name of the config server's property source (file.yml) that we want to use 15 | name: book 16 | profile: prod # profile(s) of the property source 17 | label: master # toggle to switch to a different version of the configuration as stored in git 18 | # it can be set to any label, branch or commit of the configuration source Git repository 19 | 20 | jhipster: 21 | registry: 22 | password: admin 23 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/jwt/JWTConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security.jwt; 2 | 3 | import org.springframework.security.config.annotation.SecurityConfigurerAdapter; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.web.DefaultSecurityFilterChain; 6 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 7 | 8 | public class JWTConfigurer extends SecurityConfigurerAdapter { 9 | 10 | private final TokenProvider tokenProvider; 11 | 12 | public JWTConfigurer(TokenProvider tokenProvider) { 13 | this.tokenProvider = tokenProvider; 14 | } 15 | 16 | @Override 17 | public void configure(HttpSecurity http) { 18 | JWTFilter customFilter = new JWTFilter(tokenProvider); 19 | http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/web/rest/dto/InStockBookDTOTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.dto; 2 | 3 | import com.skcc.book.web.rest.dto.InStockBookDTO; 4 | import org.junit.jupiter.api.Test; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | import com.skcc.book.web.rest.TestUtil; 7 | 8 | public class InStockBookDTOTest { 9 | 10 | @Test 11 | public void dtoEqualsVerifier() throws Exception { 12 | TestUtil.equalsVerifier(InStockBookDTO.class); 13 | InStockBookDTO inStockBookDTO1 = new InStockBookDTO(); 14 | inStockBookDTO1.setId(1L); 15 | InStockBookDTO inStockBookDTO2 = new InStockBookDTO(); 16 | assertThat(inStockBookDTO1).isNotEqualTo(inStockBookDTO2); 17 | inStockBookDTO2.setId(inStockBookDTO1.getId()); 18 | assertThat(inStockBookDTO1).isEqualTo(inStockBookDTO2); 19 | inStockBookDTO2.setId(2L); 20 | assertThat(inStockBookDTO1).isNotEqualTo(inStockBookDTO2); 21 | inStockBookDTO1.setId(null); 22 | assertThat(inStockBookDTO1).isNotEqualTo(inStockBookDTO2); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/docker/monitoring.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | book-prometheus: 4 | image: prom/prometheus:v2.16.0 5 | volumes: 6 | - ./prometheus/:/etc/prometheus/ 7 | command: 8 | - '--config.file=/etc/prometheus/prometheus.yml' 9 | ports: 10 | - 9090:9090 11 | # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and 12 | # grafana/provisioning/datasources/datasource.yml 13 | network_mode: 'host' # to test locally running service 14 | book-grafana: 15 | image: grafana/grafana:6.6.2 16 | volumes: 17 | - ./grafana/provisioning/:/etc/grafana/provisioning/ 18 | environment: 19 | - GF_SECURITY_ADMIN_PASSWORD=admin 20 | - GF_USERS_ALLOW_SIGN_UP=false 21 | - GF_INSTALL_PLUGINS=grafana-piechart-panel 22 | ports: 23 | - 3000:3000 24 | # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and 25 | # grafana/provisioning/datasources/datasource.yml 26 | network_mode: 'host' # to test locally running service 27 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/fake-data/in_stock_book.csv: -------------------------------------------------------------------------------- 1 | id;title;description;author;publisher;isbn;publication_date;source 2 | 1;experiences;calculating transition connecting;Rubber;lime Yemen;6574;2020-06-09;Donated 3 | 2;Engineer;4th generation Legacy;utilisation white;indigo;13972;2020-06-10;Purchased 4 | 3;Mobility strategy mission-critical;Savings Account hybrid;Argentine Peso;Switzerland Games fuchsia;88340;2020-06-09;Donated 5 | 4;Quality;Facilitator system-worthy Dong;Synchronised Chair Kids;Buckinghamshire Data Accounts;71635;2020-06-09;Purchased 6 | 5;SAS;Bahraini Dinar Direct channels;Home;connecting JSON;51597;2020-06-09;Donated 7 | 6;bypassing;Seamless monetize;Concrete virtual;Cambridgeshire;45007;2020-06-09;Purchased 8 | 7;Dynamic protocol Burgs;Chief haptic;flexibility;Sierra Leone Metal;36288;2020-06-10;Purchased 9 | 8;Unbranded;human-resource whiteboard Electronics;invoice Lakes Director;sensor;94622;2020-06-10;Purchased 10 | 9;Developer Quality;panel migration Garden;Rustic;SQL Communications Future;7167;2020-06-10;Donated 11 | 10;Kina Wyoming fuchsia;eyeballs;Timor-Leste;SQL;35922;2020-06-09;Donated 12 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/ArchTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book; 2 | 3 | import com.tngtech.archunit.core.domain.JavaClasses; 4 | import com.tngtech.archunit.core.importer.ClassFileImporter; 5 | import com.tngtech.archunit.core.importer.ImportOption; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; 9 | 10 | class ArchTest { 11 | 12 | @Test 13 | void servicesAndRepositoriesShouldNotDependOnWebLayer() { 14 | 15 | JavaClasses importedClasses = new ClassFileImporter() 16 | .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) 17 | .importPackages("com.skcc.book"); 18 | 19 | noClasses() 20 | .that() 21 | .resideInAnyPackage("com.skcc.book.service..") 22 | .or() 23 | .resideInAnyPackage("com.skcc.book.repository..") 24 | .should().dependOnClassesThat() 25 | .resideInAnyPackage("..com.skcc.book.web..") 26 | .because("Services and repositories should not depend on web layer") 27 | .check(importedClasses); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/CloudDatabaseConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import io.github.jhipster.config.JHipsterConstants; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.cache.CacheManager; 8 | import org.springframework.cloud.config.java.AbstractCloudConfig; 9 | import org.springframework.context.annotation.*; 10 | 11 | import javax.sql.DataSource; 12 | import org.springframework.boot.context.properties.ConfigurationProperties; 13 | 14 | 15 | @Configuration 16 | @Profile(JHipsterConstants.SPRING_PROFILE_CLOUD) 17 | public class CloudDatabaseConfiguration extends AbstractCloudConfig { 18 | 19 | private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class); 20 | 21 | private static final String CLOUD_CONFIGURATION_HIKARI_PREFIX = "spring.datasource.hikari"; 22 | 23 | @Bean 24 | @ConfigurationProperties(CLOUD_CONFIGURATION_HIKARI_PREFIX) 25 | public DataSource dataSource(CacheManager cacheManager) { 26 | log.info("Configuring JDBC datasource from a cloud provider"); 27 | return connectionFactory().dataSource(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/docker/jhipster-registry.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | jhipster-registry: 4 | image: jhipster/jhipster-registry:v6.1.2 5 | volumes: 6 | - ./central-server-config:/central-config 7 | # When run with the "dev" Spring profile, the JHipster Registry will 8 | # read the config from the local filesystem (central-server-config directory) 9 | # When run with the "prod" Spring profile, it will read the configuration from a Git repository 10 | # See https://www.jhipster.tech/jhipster-registry/#spring-cloud-config 11 | environment: 12 | - _JAVA_OPTIONS=-Xmx512m -Xms256m 13 | - SPRING_PROFILES_ACTIVE=dev,swagger 14 | - SPRING_SECURITY_USER_PASSWORD=admin 15 | - JHIPSTER_REGISTRY_PASSWORD=admin 16 | - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=native 17 | - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_LOCATIONS=file:./central-config/localhost-config/ 18 | # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git 19 | # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI=https://github.com/jhipster/jhipster-registry/ 20 | # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_PATHS=central-config 21 | ports: 22 | - 8761:8761 23 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/LocaleConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import io.github.jhipster.config.locale.AngularCookieLocaleResolver; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.servlet.LocaleResolver; 8 | import org.springframework.web.servlet.config.annotation.*; 9 | import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; 10 | 11 | @Configuration 12 | public class LocaleConfiguration implements WebMvcConfigurer { 13 | 14 | @Bean(name = "localeResolver") 15 | public LocaleResolver localeResolver() { 16 | AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); 17 | cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); 18 | return cookieLocaleResolver; 19 | } 20 | 21 | @Override 22 | public void addInterceptors(InterceptorRegistry registry) { 23 | LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); 24 | localeChangeInterceptor.setParamName("language"); 25 | registry.addInterceptor(localeChangeInterceptor); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/i18n/messages_ko.properties: -------------------------------------------------------------------------------- 1 | # Error page 2 | error.title=\uc694\uccad\uc774 \uc9c4\ud589\ub418\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4 3 | error.subtitle=\uc8c4\uc1a1\ud569\ub2c8\ub2e4, \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4. 4 | error.status=\uc0c1\ud0dc: 5 | error.message=\uba54\uc138\uc9c0: 6 | 7 | # Activation email 8 | email.activation.title=book \uc778\uc99d 9 | email.activation.greeting={0}\ub2d8 \uc548\ub155\ud558\uc138\uc694 10 | email.activation.text1=book \uacc4\uc815\uc774 \uc0dd\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \uc544\ub798 URL\uc744 \ud074\ub9ad\ud574 \uc778\uc99d\uc744 \uc644\ub8cc\ud574 \uc8fc\uc138\uc694. 11 | email.activation.text2=\uac10\uc0ac\ud569\ub2c8\ub2e4, 12 | email.signature=book. 13 | 14 | # Creation email 15 | email.creation.text1=Your book account has been created, please click on the URL below to access it: 16 | 17 | # Reset email 18 | email.reset.title=book \ube44\ubc00\ubc88\ud638 \ubcc0\uacbd 19 | email.reset.greeting={0}\ub2d8 \uc548\ub155\ud558\uc138\uc694 20 | email.reset.text1=book \uacc4\uc815 \ube44\ubc00\ubc88\ud638 \ubcc0\uacbd \uc694\uccad\uc744 \ud558\uc168\uc2b5\ub2c8\ub2e4. \uc544\ub798 URL\uc744 \ud074\ub9ad\ud574 \ube44\ubc00\ubc88\ud638\ub97c \ubcc0\uacbd\ud574 \uc8fc\uc138\uc694: 21 | email.reset.text2=\uac10\uc0ac\ud569\ub2c8\ub2e4. 22 | -------------------------------------------------------------------------------- /jhipster-jdl-2.jh: -------------------------------------------------------------------------------- 1 | entity Book { 2 | title String, 3 | description String, 4 | author String, 5 | publisher String, 6 | isbn Long, 7 | publicationDate LocalDate, 8 | classification Classification, 9 | bookStatus BookStatus, 10 | location Location 11 | } 12 | 13 | 14 | entity InStockBook { 15 | title String, 16 | description String, 17 | author String, 18 | publisher String, 19 | isbn Long, 20 | publicationDate LocalDate, 21 | source Source 22 | } 23 | 24 | // an ignored comment 25 | /** not an ignored comment */ 26 | enum Classification { 27 | Arts, 28 | Photography, 29 | Biographies, 30 | BusinessMoney, 31 | Children, 32 | ComputerTechnology, 33 | History, 34 | Medical, 35 | Travel, 36 | Romance, 37 | Science, 38 | Math, 39 | SelfHelp 40 | } 41 | 42 | enum BookStatus{ 43 | AVAILABLE, 44 | UNAVAILABLE 45 | } 46 | 47 | enum Source{ 48 | Donated, 49 | Purchased 50 | } 51 | 52 | enum Location{ 53 | JEONGJA, 54 | PANGYO 55 | } 56 | // Set pagination options 57 | paginate * with pagination 58 | 59 | // Use Data Transfert Objects (DTO) 60 | dto * with mapstruct 61 | 62 | // Set service options to all except few 63 | service all with serviceImpl 64 | 65 | // Set an angular suffix 66 | // angularSuffix * with mySuffix 67 | -------------------------------------------------------------------------------- /.jhipster/InStockBook.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "InStockBook", 3 | "fields": [ 4 | { 5 | "fieldName": "title", 6 | "fieldType": "String" 7 | }, 8 | { 9 | "fieldName": "description", 10 | "fieldType": "String" 11 | }, 12 | { 13 | "fieldName": "author", 14 | "fieldType": "String" 15 | }, 16 | { 17 | "fieldName": "publisher", 18 | "fieldType": "String" 19 | }, 20 | { 21 | "fieldName": "isbn", 22 | "fieldType": "Long" 23 | }, 24 | { 25 | "fieldName": "publicationDate", 26 | "fieldType": "LocalDate" 27 | }, 28 | { 29 | "fieldName": "source", 30 | "fieldType": "Source", 31 | "fieldValues": "Donated,Purchased" 32 | } 33 | ], 34 | "relationships": [], 35 | "changelogDate": "20200610085200", 36 | "entityTableName": "in_stock_book", 37 | "dto": "mapstruct", 38 | "pagination": "pagination", 39 | "service": "serviceImpl", 40 | "jpaMetamodelFiltering": false, 41 | "fluentMethods": true, 42 | "readOnly": false, 43 | "embedded": false, 44 | "clientRootFolder": "book", 45 | "applications": "*", 46 | "microserviceName": "book" 47 | } -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/service/InStockBookService.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.service; 2 | 3 | import com.skcc.book.domain.InStockBook; 4 | import com.skcc.book.web.rest.dto.InStockBookDTO; 5 | 6 | import org.springframework.data.domain.Page; 7 | import org.springframework.data.domain.Pageable; 8 | 9 | import java.util.Optional; 10 | 11 | /** 12 | * Service Interface for managing {@link com.skcc.book.domain.InStockBook}. 13 | */ 14 | public interface InStockBookService { 15 | 16 | /** 17 | * Save a inStockBook. 18 | * 19 | * @param inStockBook the entity to save. 20 | * @return the persisted entity. 21 | */ 22 | InStockBook save(InStockBook inStockBook); 23 | 24 | /** 25 | * Get all the inStockBooks. 26 | * 27 | * @param pageable the pagination information. 28 | * @return the list of entities. 29 | */ 30 | Page findAll(Pageable pageable); 31 | 32 | /** 33 | * Get the "id" inStockBook. 34 | * 35 | * @param id the id of the entity. 36 | * @return the entity. 37 | */ 38 | InStockBook findOne(Long id); 39 | 40 | /** 41 | * Delete the "id" inStockBook. 42 | * 43 | * @param id the id of the entity. 44 | */ 45 | void delete(Long id); 46 | 47 | Page findByTitle(String title, Pageable pageable); 48 | } 49 | -------------------------------------------------------------------------------- /src/main/docker/app.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | book-app: 4 | image: book 5 | environment: 6 | - _JAVA_OPTIONS=-Xmx512m -Xms256m 7 | - SPRING_PROFILES_ACTIVE=prod,swagger 8 | - MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true 9 | - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/eureka 10 | - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config 11 | - SPRING_DATASOURCE_URL=jdbc:mariadb://book-mariadb:3306/book 12 | - JHIPSTER_SLEEP=120 # gives time for mariadb server to start 13 | - KAFKA_BOOTSTRAPSERVERS=kafka:9092 14 | book-mariadb: 15 | extends: 16 | file: mariadb.yml 17 | service: book-mariadb 18 | kafka: 19 | extends: 20 | file: kafka.yml 21 | service: kafka 22 | environment: 23 | - KAFKA_ADVERTISED_HOST_NAME=kafka 24 | - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 25 | zookeeper: 26 | extends: 27 | file: kafka.yml 28 | service: zookeeper 29 | jhipster-registry: 30 | extends: 31 | file: jhipster-registry.yml 32 | service: jhipster-registry 33 | environment: 34 | - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=native 35 | - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_LOCATIONS=file:./central-config/docker-config/ 36 | -------------------------------------------------------------------------------- /src/main/resources/config/bootstrap.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Spring Cloud Config bootstrap configuration for the "dev" profile 3 | # In prod profile, properties will be overwritten by the ones defined in bootstrap-prod.yml 4 | # =================================================================== 5 | 6 | jhipster: 7 | registry: 8 | password: admin 9 | 10 | spring: 11 | application: 12 | name: book 13 | profiles: 14 | # The commented value for `active` can be replaced with valid Spring profiles to load. 15 | # Otherwise, it will be filled in by maven when building the JAR file 16 | # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` 17 | active: #spring.profiles.active# 18 | cloud: 19 | config: 20 | fail-fast: false # if not in "prod" profile, do not force to use Spring Cloud Config 21 | uri: http://admin:${jhipster.registry.password}@localhost:8761/config 22 | # name of the config server's property source (file.yml) that we want to use 23 | name: book 24 | profile: dev # profile(s) of the property source 25 | label: master # toggle to switch to a different version of the configuration as stored in git 26 | # it can be set to any label, branch or commit of the configuration source Git repository 27 | -------------------------------------------------------------------------------- /src/main/docker/prometheus/prometheus.yml: -------------------------------------------------------------------------------- 1 | # Sample global config for monitoring JHipster applications 2 | global: 3 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 4 | evaluation_interval: 15s # By default, scrape targets every 15 seconds. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Attach these labels to any time series or alerts when communicating with 8 | # external systems (federation, remote storage, Alertmanager). 9 | external_labels: 10 | monitor: 'jhipster' 11 | 12 | # A scrape configuration containing exactly one endpoint to scrape: 13 | # Here it's Prometheus itself. 14 | scrape_configs: 15 | # The job name is added as a label `job=` to any timeseries scraped from this config. 16 | - job_name: 'prometheus' 17 | 18 | # Override the global default and scrape targets from this job every 5 seconds. 19 | scrape_interval: 5s 20 | 21 | # scheme defaults to 'http' enable https in case your application is server via https 22 | #scheme: https 23 | # basic auth is not needed by default. See https://www.jhipster.tech/monitoring/#configuring-metrics-forwarding for details 24 | #basic_auth: 25 | # username: admin 26 | # password: admin 27 | metrics_path: /management/prometheus 28 | static_configs: 29 | - targets: 30 | # On MacOS, replace localhost by host.docker.internal 31 | - localhost:8081 32 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/fake-data/book.csv: -------------------------------------------------------------------------------- 1 | id;title;description;author;publisher;isbn;publication_date;classification;book_status;location;book_reservation; 2 | 1;Handmade Cotton Table CSS;Mayotte Dynamic SAS;digital forecast Bahrain;Principal Baht radical;78204;2020-05-05;Arts;UNAVAILABLE;JEONGJA; 3 | 2;ADP Texas auxiliary;Pula redefine Paradigm;Optimization secondary driver;Soft Alaska;2840;2020-05-06;Biographies;AVAILABLE;PANGYO; 4 | 3;cross-platform;Consultant XML methodologies;orchestrate application Port;payment Small Soft Keyboard;49455;2020-05-05;Travel;AVAILABLE;JEONGJA; 5 | 4;Czech Koruna;Soap Producer hard drive;mobile Product programming;Checking Account Industrial;71630;2020-05-05;Science;AVAILABLE;JEONGJA; 6 | 5;parse world-class Nebraska;South Carolina;interface;Som;89992;2020-05-06;Arts;AVAILABLE;PANGYO; 7 | 6;backing up;Auto Loan Account generate Health;Cambridgeshire;circuit Planner optical;52271;2020-05-05;Medical;AVAILABLE;PANGYO; 8 | 7;Oregon HDD;wireless Netherlands networks;copying Rand Namibia Dollar;methodical Concrete Saint Helena Pound;96202;2020-05-06;SelfHelp;UNAVAILABLE;PANGYO; 9 | 8;Western Sahara program Technician;digital vortals initiatives;composite;Iceland Avon;76508;2020-05-06;History;AVAILABLE;PANGYO; 10 | 9;Computers;Sausages Tuna;Soft Berkshire Morocco;California;9917;2020-05-06;History;UNAVAILABLE;JEONGJA; 11 | 10;quantify;olive;Coordinator;Costa Rica green;76484;2020-05-05;Arts;AVAILABLE;JEONGJA; 12 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-jhipster": { 3 | "promptValues": { 4 | "packageName": "com.skcc.book", 5 | "nativeLanguage": "ko" 6 | }, 7 | "jhipsterVersion": "6.8.0", 8 | "applicationType": "microservice", 9 | "baseName": "book", 10 | "packageName": "com.skcc.book", 11 | "packageFolder": "com/skcc/book", 12 | "serverPort": "8081", 13 | "authenticationType": "jwt", 14 | "cacheProvider": "hazelcast", 15 | "enableHibernateCache": true, 16 | "websocket": false, 17 | "databaseType": "sql", 18 | "devDatabaseType": "h2Memory", 19 | "prodDatabaseType": "mariadb", 20 | "searchEngine": false, 21 | "messageBroker": "kafka", 22 | "serviceDiscoveryType": "eureka", 23 | "buildTool": "maven", 24 | "enableSwaggerCodegen": false, 25 | "jwtSecretKey": "ZDI0MmE4ZDIzMzQ5OTRmZThlZjU5NzZlMDY3NDEzOTYxMDhlNmZjOGYwNzE3ODBkMGM4NGJhMTY5NDBkYWEzMjg2MmYxODljNmI4NGJlY2JlZmRmNGUxN2E3NWRmNzY0ZjZiYmExZjQxMzgyMTU2NjZiMzY0OTIzYmE1ZTUxN2Q=", 26 | "embeddableLaunchScript": false, 27 | "creationTimestamp": 1588748895294, 28 | "testFrameworks": [], 29 | "jhiPrefix": "jhi", 30 | "entitySuffix": "", 31 | "dtoSuffix": "DTO", 32 | "otherModules": [], 33 | "enableTranslation": true, 34 | "clientPackageManager": "npm", 35 | "nativeLanguage": "ko", 36 | "languages": ["ko", "en"], 37 | "blueprints": [], 38 | "skipClient": true, 39 | "skipUserManagement": true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/repository/BookRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.repository; 2 | 3 | import com.skcc.book.domain.Book; 4 | import com.skcc.book.domain.enumeration.BookStatus; 5 | import com.skcc.book.domain.enumeration.Classification; 6 | import com.skcc.book.domain.enumeration.Location; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | 11 | import java.time.LocalDate; 12 | 13 | @SpringBootTest 14 | class BookRepositoryTest{ 15 | 16 | @Autowired 17 | private BookRepository bookRepository; 18 | 19 | @Test 20 | public void create(){ 21 | 22 | Book book = new Book(); 23 | book.setId((long)1); 24 | book.setPublisher("pub"); 25 | book.setAuthor("author"); 26 | book.setBookStatus(BookStatus.AVAILABLE); 27 | book.setClassification(Classification.Biographies); 28 | book.setLocation(Location.JEONGJA); 29 | book.setDescription("desc"); 30 | book.setIsbn((long)124); 31 | book.setPublicationDate(LocalDate.now()); 32 | book.setTitle("title"); 33 | 34 | book=bookRepository.save(book); 35 | System.out.println(book); 36 | 37 | } 38 | 39 | @Test 40 | public void updateBook(){ 41 | Book book = bookRepository.findById((long)1).get(); 42 | 43 | book = bookRepository.save(book); 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/master.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/resources/config/application-tls.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Activate this profile to enable TLS and HTTP/2. 3 | # 4 | # JHipster has generated a self-signed certificate, which will be used to encrypt traffic. 5 | # As your browser will not understand this certificate, you will need to import it. 6 | # 7 | # Another (easiest) solution with Chrome is to enable the "allow-insecure-localhost" flag 8 | # at chrome://flags/#allow-insecure-localhost 9 | # =================================================================== 10 | server: 11 | ssl: 12 | key-store: classpath:config/tls/keystore.p12 13 | key-store-password: password 14 | key-store-type: PKCS12 15 | key-alias: selfsigned 16 | ciphers: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 17 | enabled-protocols: TLSv1.2 18 | http2: 19 | enabled: true 20 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/errors/BadRequestAlertException.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.errors; 2 | 3 | import org.zalando.problem.AbstractThrowableProblem; 4 | import org.zalando.problem.Status; 5 | 6 | import java.net.URI; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class BadRequestAlertException extends AbstractThrowableProblem { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | private final String entityName; 15 | 16 | private final String errorKey; 17 | 18 | public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) { 19 | this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey); 20 | } 21 | 22 | public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) { 23 | super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey)); 24 | this.entityName = entityName; 25 | this.errorKey = errorKey; 26 | } 27 | 28 | public String getEntityName() { 29 | return entityName; 30 | } 31 | 32 | public String getErrorKey() { 33 | return errorKey; 34 | } 35 | 36 | private static Map getAlertParameters(String entityName, String errorKey) { 37 | Map parameters = new HashMap<>(); 38 | parameters.put("message", "error." + errorKey); 39 | parameters.put("params", entityName); 40 | return parameters; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/KafkaProperties.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | @Configuration 10 | @ConfigurationProperties(prefix = "kafka") 11 | public class KafkaProperties { 12 | 13 | private String bootStrapServers = "localhost:9092"; 14 | 15 | private Map consumer = new HashMap<>(); 16 | 17 | private Map producer = new HashMap<>(); 18 | 19 | public String getBootStrapServers() { 20 | return bootStrapServers; 21 | } 22 | 23 | public void setBootStrapServers(String bootStrapServers) { 24 | this.bootStrapServers = bootStrapServers; 25 | } 26 | 27 | public Map getConsumerProps() { 28 | Map properties = new HashMap<>(this.consumer); 29 | if (!properties.containsKey("bootstrap.servers")) { 30 | properties.put("bootstrap.servers", this.bootStrapServers); 31 | } 32 | return properties; 33 | } 34 | 35 | public void setConsumer(Map consumer) { 36 | this.consumer = consumer; 37 | } 38 | 39 | public Map getProducerProps() { 40 | Map properties = new HashMap<>(this.producer); 41 | if (!properties.containsKey("bootstrap.servers")) { 42 | properties.put("bootstrap.servers", this.bootStrapServers); 43 | } 44 | return properties; 45 | } 46 | 47 | public void setProducer(Map producer) { 48 | this.producer = producer; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/docker/grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | # list of datasources that should be deleted from the database 4 | deleteDatasources: 5 | - name: Prometheus 6 | orgId: 1 7 | 8 | # list of datasources to insert/update depending 9 | # whats available in the database 10 | datasources: 11 | # name of the datasource. Required 12 | - name: Prometheus 13 | # datasource type. Required 14 | type: prometheus 15 | # access mode. direct or proxy. Required 16 | access: proxy 17 | # org id. will default to orgId 1 if not specified 18 | orgId: 1 19 | # url 20 | # On MacOS, replace localhost by host.docker.internal 21 | url: http://localhost:9090 22 | # database password, if used 23 | password: 24 | # database user, if used 25 | user: 26 | # database name, if used 27 | database: 28 | # enable/disable basic auth 29 | basicAuth: false 30 | # basic auth username 31 | basicAuthUser: admin 32 | # basic auth password 33 | basicAuthPassword: admin 34 | # enable/disable with credentials headers 35 | withCredentials: 36 | # mark as default datasource. Max one per org 37 | isDefault: true 38 | # fields that will be converted to json and stored in json_data 39 | jsonData: 40 | graphiteVersion: '1.1' 41 | tlsAuth: false 42 | tlsAuthWithCACert: false 43 | # json object of data that will be encrypted. 44 | secureJsonData: 45 | tlsCACert: '...' 46 | tlsClientCert: '...' 47 | tlsClientKey: '...' 48 | version: 1 49 | # allow users to edit datasources from the UI. 50 | editable: true 51 | -------------------------------------------------------------------------------- /.jhipster/Book.json: -------------------------------------------------------------------------------- 1 | { 2 | "fluentMethods": true, 3 | "clientRootFolder": "book", 4 | "relationships": [], 5 | "fields": [ 6 | { 7 | "fieldName": "title", 8 | "fieldType": "String" 9 | }, 10 | { 11 | "fieldName": "description", 12 | "fieldType": "String" 13 | }, 14 | { 15 | "fieldName": "author", 16 | "fieldType": "String" 17 | }, 18 | { 19 | "fieldName": "publisher", 20 | "fieldType": "String" 21 | }, 22 | { 23 | "fieldName": "isbn", 24 | "fieldType": "Long" 25 | }, 26 | { 27 | "fieldName": "publicationDate", 28 | "fieldType": "LocalDate" 29 | }, 30 | { 31 | "fieldName": "classification", 32 | "fieldType": "Classification", 33 | "fieldValues": "Arts,Photography,Biographies,BusinessMoney,Children,ComputerTechnology,History,Medical,Travel,Romance,Science,Math,SelfHelp" 34 | }, 35 | { 36 | "fieldName": "bookStatus", 37 | "fieldType": "BookStatus", 38 | "fieldValues": "AVAILABLE,UNAVAILABLE" 39 | }, 40 | { 41 | "fieldName": "location", 42 | "fieldType": "Location", 43 | "fieldValues": "JEONGJA,PANGYO" 44 | } 45 | ], 46 | "changelogDate": "20200506084946", 47 | "dto": "mapstruct", 48 | "searchEngine": false, 49 | "service": "serviceImpl", 50 | "entityTableName": "book", 51 | "databaseType": "sql", 52 | "readOnly": false, 53 | "jpaMetamodelFiltering": false, 54 | "pagination": "pagination", 55 | "microserviceName": "book", 56 | "name": "Book", 57 | "embedded": false, 58 | "applications": "*" 59 | } 60 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=book 2 | sonar.projectName=book generated by jhipster 3 | sonar.projectVersion=1.0 4 | 5 | sonar.sources=src/main/ 6 | sonar.host.url=http://localhost:9001 7 | 8 | sonar.tests=src/test/ 9 | sonar.coverage.jacoco.xmlReportPaths=target/jacoco/test/jacoco.xml,target/jacoco/integrationTest/jacoco.xml 10 | sonar.java.codeCoveragePlugin=jacoco 11 | sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest 12 | 13 | sonar.sourceEncoding=UTF-8 14 | sonar.exclusions=src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, target/classes/static/**/*.* 15 | 16 | sonar.issue.ignore.multicriteria=S3437,S4502,S4684,UndocumentedApi 17 | # Rule https://sonarcloud.io/coding_rules?open=squid%3AS3437&rule_key=squid%3AS3437 is ignored, as a JPA-managed field cannot be transient 18 | sonar.issue.ignore.multicriteria.S3437.resourceKey=src/main/java/**/* 19 | sonar.issue.ignore.multicriteria.S3437.ruleKey=squid:S3437 20 | # Rule https://sonarcloud.io/coding_rules?open=squid%3AUndocumentedApi&rule_key=squid%3AUndocumentedApi is ignored, as we want to follow "clean code" guidelines and classes, methods and arguments names should be self-explanatory 21 | sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey=src/main/java/**/* 22 | sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey=squid:UndocumentedApi 23 | # Rule https://sonarcloud.io/coding_rules?open=squid%3AS4502&rule_key=squid%3AS4502 is ignored, as for JWT tokens we are not subject to CSRF attack 24 | sonar.issue.ignore.multicriteria.S4502.resourceKey=src/main/java/**/* 25 | sonar.issue.ignore.multicriteria.S4502.ruleKey=squid:S4502 26 | # Rule https://sonarcloud.io/coding_rules?open=squid%3AS4684&rule_key=squid%3AS4684 27 | sonar.issue.ignore.multicriteria.S4684.resourceKey=src/main/java/**/* 28 | sonar.issue.ignore.multicriteria.S4684.ruleKey=squid:S4684 29 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/JacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 4 | import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import com.fasterxml.jackson.module.afterburner.AfterburnerModule; 7 | 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.zalando.problem.ProblemModule; 11 | import org.zalando.problem.violations.ConstraintViolationProblemModule; 12 | 13 | @Configuration 14 | public class JacksonConfiguration { 15 | 16 | /** 17 | * Support for Java date and time API. 18 | * @return the corresponding Jackson module. 19 | */ 20 | @Bean 21 | public JavaTimeModule javaTimeModule() { 22 | return new JavaTimeModule(); 23 | } 24 | 25 | @Bean 26 | public Jdk8Module jdk8TimeModule() { 27 | return new Jdk8Module(); 28 | } 29 | 30 | /* 31 | * Support for Hibernate types in Jackson. 32 | */ 33 | @Bean 34 | public Hibernate5Module hibernate5Module() { 35 | return new Hibernate5Module(); 36 | } 37 | 38 | /* 39 | * Jackson Afterburner module to speed up serialization/deserialization. 40 | */ 41 | @Bean 42 | public AfterburnerModule afterburnerModule() { 43 | return new AfterburnerModule(); 44 | } 45 | 46 | /* 47 | * Module for serialization/deserialization of RFC7807 Problem. 48 | */ 49 | @Bean 50 | ProblemModule problemModule() { 51 | return new ProblemModule(); 52 | } 53 | 54 | /* 55 | * Module for serialization/deserialization of ConstraintViolationProblem. 56 | */ 57 | @Bean 58 | ConstraintViolationProblemModule constraintViolationProblemModule() { 59 | return new ConstraintViolationProblemModule(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/service/BookService.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.service; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.skcc.book.domain.Book; 5 | 6 | import com.skcc.book.web.rest.dto.BookInfoDTO; 7 | import org.springframework.data.domain.Page; 8 | import org.springframework.data.domain.Pageable; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.concurrent.ExecutionException; 13 | 14 | /** 15 | * Service Interface for managing {@link com.skcc.book.domain.Book}. 16 | */ 17 | public interface BookService { 18 | 19 | /** 20 | * Save a book. 21 | * 22 | * @param book the entity to save. 23 | * @return the persisted entity. 24 | */ 25 | Book save(Book book); 26 | 27 | /** 28 | * Get all the books. 29 | * 30 | * @param pageable the pagination information. 31 | * @return the list of entities. 32 | */ 33 | Page findAll(Pageable pageable); 34 | 35 | /** 36 | * Get the "id" book. 37 | * 38 | * @param id the id of the entity. 39 | * @return the entity. 40 | */ 41 | Optional findOne(Long id); 42 | 43 | /** 44 | * Delete the "id" book. 45 | * 46 | * @param id the id of the entity. 47 | */ 48 | void delete(Long id) throws InterruptedException, ExecutionException, JsonProcessingException; 49 | 50 | Book createBook(Book book) throws InterruptedException, ExecutionException, JsonProcessingException; 51 | 52 | Book updateBook(Book book) throws InterruptedException, ExecutionException, JsonProcessingException; 53 | 54 | void processChangeBookState(Long bookId, String bookStatus); 55 | 56 | 57 | Book findBookInfo(Long bookId); 58 | 59 | Book registerNewBook(Book book, Long inStockId) throws InterruptedException, ExecutionException, JsonProcessingException; 60 | 61 | void sendBookCatalogEvent(String eventType, Long bookId) throws InterruptedException, ExecutionException, JsonProcessingException; 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/jwt/JWTFilter.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security.jwt; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | import org.springframework.util.StringUtils; 6 | import org.springframework.web.filter.GenericFilterBean; 7 | 8 | import javax.servlet.FilterChain; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.ServletRequest; 11 | import javax.servlet.ServletResponse; 12 | import javax.servlet.http.HttpServletRequest; 13 | import java.io.IOException; 14 | 15 | /** 16 | * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is 17 | * found. 18 | */ 19 | public class JWTFilter extends GenericFilterBean { 20 | 21 | public static final String AUTHORIZATION_HEADER = "Authorization"; 22 | 23 | private final TokenProvider tokenProvider; 24 | 25 | public JWTFilter(TokenProvider tokenProvider) { 26 | this.tokenProvider = tokenProvider; 27 | } 28 | 29 | @Override 30 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 31 | throws IOException, ServletException { 32 | HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; 33 | String jwt = resolveToken(httpServletRequest); 34 | if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { 35 | Authentication authentication = this.tokenProvider.getAuthentication(jwt); 36 | SecurityContextHolder.getContext().setAuthentication(authentication); 37 | } 38 | filterChain.doFilter(servletRequest, servletResponse); 39 | } 40 | 41 | private String resolveToken(HttpServletRequest request) { 42 | String bearerToken = request.getHeader(AUTHORIZATION_HEADER); 43 | if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { 44 | return bearerToken.substring(7); 45 | } 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/AsyncConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; 7 | import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; 8 | import org.springframework.boot.autoconfigure.task.TaskExecutionProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.scheduling.annotation.AsyncConfigurer; 12 | import org.springframework.scheduling.annotation.EnableAsync; 13 | import org.springframework.scheduling.annotation.EnableScheduling; 14 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 15 | 16 | import java.util.concurrent.Executor; 17 | 18 | @Configuration 19 | @EnableAsync 20 | @EnableScheduling 21 | public class AsyncConfiguration implements AsyncConfigurer { 22 | 23 | private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class); 24 | 25 | private final TaskExecutionProperties taskExecutionProperties; 26 | 27 | public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) { 28 | this.taskExecutionProperties = taskExecutionProperties; 29 | } 30 | 31 | @Override 32 | @Bean(name = "taskExecutor") 33 | public Executor getAsyncExecutor() { 34 | log.debug("Creating Async Task Executor"); 35 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 36 | executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize()); 37 | executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize()); 38 | executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity()); 39 | executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix()); 40 | return new ExceptionHandlingAsyncTaskExecutor(executor); 41 | } 42 | 43 | @Override 44 | public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 45 | return new SimpleAsyncUncaughtExceptionHandler(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | WARN 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/DatabaseConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import io.github.jhipster.config.JHipsterConstants; 4 | import io.github.jhipster.config.h2.H2ConfigurationHelper; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Profile; 10 | 11 | import org.springframework.core.env.Environment; 12 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 13 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 14 | import org.springframework.transaction.annotation.EnableTransactionManagement; 15 | 16 | import java.sql.SQLException; 17 | 18 | @Configuration 19 | @EnableJpaRepositories("com.skcc.book.repository") 20 | @EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware") 21 | @EnableTransactionManagement 22 | public class DatabaseConfiguration { 23 | 24 | private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); 25 | 26 | private final Environment env; 27 | 28 | public DatabaseConfiguration(Environment env) { 29 | this.env = env; 30 | } 31 | 32 | /** 33 | * Open the TCP port for the H2 database, so it is available remotely. 34 | * 35 | * @return the H2 database TCP server. 36 | * @throws SQLException if the server failed to start. 37 | */ 38 | @Bean(initMethod = "start", destroyMethod = "stop") 39 | @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) 40 | public Object h2TCPServer() throws SQLException { 41 | String port = getValidPortForH2(); 42 | log.debug("H2 database is available on port {}", port); 43 | return H2ConfigurationHelper.createServer(port); 44 | } 45 | 46 | private String getValidPortForH2() { 47 | int port = Integer.parseInt(env.getProperty("server.port")); 48 | if (port < 10000) { 49 | port = 10000 + port; 50 | } else { 51 | if (port < 63536) { 52 | port = port + 2000; 53 | } else { 54 | port = port - 2000; 55 | } 56 | } 57 | return String.valueOf(port); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/web/rest/errors/ExceptionTranslatorTestController.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.errors; 2 | 3 | import org.springframework.dao.ConcurrencyFailureException; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.security.access.AccessDeniedException; 6 | import org.springframework.security.authentication.BadCredentialsException; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import javax.validation.Valid; 10 | import javax.validation.constraints.NotNull; 11 | 12 | @RestController 13 | @RequestMapping("/api/exception-translator-test") 14 | public class ExceptionTranslatorTestController { 15 | 16 | @GetMapping("/concurrency-failure") 17 | public void concurrencyFailure() { 18 | throw new ConcurrencyFailureException("test concurrency failure"); 19 | } 20 | 21 | @PostMapping("/method-argument") 22 | public void methodArgument(@Valid @RequestBody TestDTO testDTO) { 23 | } 24 | 25 | @GetMapping("/missing-servlet-request-part") 26 | public void missingServletRequestPartException(@RequestPart String part) { 27 | } 28 | 29 | @GetMapping("/missing-servlet-request-parameter") 30 | public void missingServletRequestParameterException(@RequestParam String param) { 31 | } 32 | 33 | @GetMapping("/access-denied") 34 | public void accessdenied() { 35 | throw new AccessDeniedException("test access denied!"); 36 | } 37 | 38 | @GetMapping("/unauthorized") 39 | public void unauthorized() { 40 | throw new BadCredentialsException("test authentication failed!"); 41 | } 42 | 43 | @GetMapping("/response-status") 44 | public void exceptionWithResponseStatus() { 45 | throw new TestResponseStatusException(); 46 | } 47 | 48 | @GetMapping("/internal-server-error") 49 | public void internalServerError() { 50 | throw new RuntimeException(); 51 | } 52 | 53 | public static class TestDTO { 54 | 55 | @NotNull 56 | private String test; 57 | 58 | public String getTest() { 59 | return test; 60 | } 61 | 62 | public void setTest(String test) { 63 | this.test = test; 64 | } 65 | } 66 | 67 | @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "test response status") 68 | @SuppressWarnings("serial") 69 | public static class TestResponseStatusException extends RuntimeException { 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/adaptor/BookProducerImpl.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.adaptor; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.skcc.book.config.KafkaProperties; 6 | import com.skcc.book.domain.event.BookChanged; 7 | import org.apache.kafka.clients.producer.KafkaProducer; 8 | import org.apache.kafka.clients.producer.ProducerRecord; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.stereotype.Service; 12 | 13 | import javax.annotation.PostConstruct; 14 | import javax.annotation.PreDestroy; 15 | import java.util.concurrent.ExecutionException; 16 | 17 | @Service 18 | public class BookProducerImpl implements BookProducer { 19 | 20 | private final Logger log = LoggerFactory.getLogger(BookProducerImpl.class); 21 | 22 | private static final String TOPIC_CATALOG = "topic_catalog"; 23 | 24 | private final KafkaProperties kafkaProperties; 25 | 26 | private final static Logger logger = LoggerFactory.getLogger(BookProducerImpl.class); 27 | private KafkaProducer producer; 28 | private final ObjectMapper objectMapper = new ObjectMapper(); 29 | 30 | 31 | public BookProducerImpl(KafkaProperties kafkaProperties) { 32 | this.kafkaProperties = kafkaProperties; 33 | } 34 | 35 | @PostConstruct 36 | public void initialize(){ 37 | log.info("Kafka producer initializing..."); 38 | this.producer = new KafkaProducer<>(kafkaProperties.getProducerProps()); 39 | Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); 40 | log.info("Kafka producer initialized"); 41 | } 42 | 43 | public void sendBookCreateEvent(BookChanged bookChanged)throws ExecutionException, InterruptedException, JsonProcessingException{ 44 | 45 | String message = objectMapper.writeValueAsString(bookChanged); 46 | producer.send(new ProducerRecord<>(TOPIC_CATALOG, message)).get(); 47 | } 48 | 49 | public void sendBookDeleteEvent(BookChanged bookDeleteEvent)throws ExecutionException, InterruptedException, JsonProcessingException{ 50 | 51 | String message = objectMapper.writeValueAsString(bookDeleteEvent); 52 | producer.send(new ProducerRecord<>(TOPIC_CATALOG, message)).get(); 53 | } 54 | 55 | @PreDestroy 56 | public void shutdown(){ 57 | log.info("Shutdown Kafka producer"); 58 | producer.close(); 59 | } 60 | 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/AbstractAuditingEntity.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import org.springframework.data.annotation.CreatedBy; 5 | import org.springframework.data.annotation.CreatedDate; 6 | import org.springframework.data.annotation.LastModifiedBy; 7 | import org.springframework.data.annotation.LastModifiedDate; 8 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 9 | 10 | import java.io.Serializable; 11 | import java.time.Instant; 12 | import javax.persistence.Column; 13 | import javax.persistence.EntityListeners; 14 | import javax.persistence.MappedSuperclass; 15 | 16 | /** 17 | * Base abstract class for entities which will hold definitions for created, last modified, created by, 18 | * last modified by attributes. 19 | */ 20 | @MappedSuperclass 21 | @EntityListeners(AuditingEntityListener.class) 22 | public abstract class AbstractAuditingEntity implements Serializable { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | @CreatedBy 27 | @Column(name = "created_by", nullable = false, length = 50, updatable = false) 28 | @JsonIgnore 29 | private String createdBy; 30 | 31 | @CreatedDate 32 | @Column(name = "created_date", updatable = false) 33 | @JsonIgnore 34 | private Instant createdDate = Instant.now(); 35 | 36 | @LastModifiedBy 37 | @Column(name = "last_modified_by", length = 50) 38 | @JsonIgnore 39 | private String lastModifiedBy; 40 | 41 | @LastModifiedDate 42 | @Column(name = "last_modified_date") 43 | @JsonIgnore 44 | private Instant lastModifiedDate = Instant.now(); 45 | 46 | public String getCreatedBy() { 47 | return createdBy; 48 | } 49 | 50 | public void setCreatedBy(String createdBy) { 51 | this.createdBy = createdBy; 52 | } 53 | 54 | public Instant getCreatedDate() { 55 | return createdDate; 56 | } 57 | 58 | public void setCreatedDate(Instant createdDate) { 59 | this.createdDate = createdDate; 60 | } 61 | 62 | public String getLastModifiedBy() { 63 | return lastModifiedBy; 64 | } 65 | 66 | public void setLastModifiedBy(String lastModifiedBy) { 67 | this.lastModifiedBy = lastModifiedBy; 68 | } 69 | 70 | public Instant getLastModifiedDate() { 71 | return lastModifiedDate; 72 | } 73 | 74 | public void setLastModifiedDate(Instant lastModifiedDate) { 75 | this.lastModifiedDate = lastModifiedDate; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/LoggingConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import io.github.jhipster.config.JHipsterProperties; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.ObjectProvider; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.boot.info.BuildProperties; 11 | import org.springframework.cloud.context.config.annotation.RefreshScope; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | import static io.github.jhipster.config.logging.LoggingUtils.*; 18 | 19 | /* 20 | * Configures the console and Logstash log appenders from the app properties 21 | */ 22 | @Configuration 23 | @RefreshScope 24 | public class LoggingConfiguration { 25 | 26 | public LoggingConfiguration(@Value("${spring.application.name}") String appName, 27 | @Value("${server.port}") String serverPort, 28 | JHipsterProperties jHipsterProperties, 29 | ObjectProvider buildProperties, 30 | ObjectMapper mapper) throws JsonProcessingException { 31 | 32 | LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); 33 | 34 | Map map = new HashMap<>(); 35 | map.put("app_name", appName); 36 | map.put("app_port", serverPort); 37 | buildProperties.ifAvailable(it -> map.put("version", it.getVersion())); 38 | String customFields = mapper.writeValueAsString(map); 39 | 40 | JHipsterProperties.Logging loggingProperties = jHipsterProperties.getLogging(); 41 | JHipsterProperties.Logging.Logstash logstashProperties = loggingProperties.getLogstash(); 42 | 43 | if (loggingProperties.isUseJsonFormat()) { 44 | addJsonConsoleAppender(context, customFields); 45 | } 46 | if (logstashProperties.isEnabled()) { 47 | addLogstashTcpSocketAppender(context, customFields, logstashProperties); 48 | } 49 | if (loggingProperties.isUseJsonFormat() || logstashProperties.isEnabled()) { 50 | addContextListener(context, customFields, loggingProperties); 51 | } 52 | if (jHipsterProperties.getMetrics().getLogs().isEnabled()) { 53 | setMetricsMarkerLogbackFilter(context, loggingProperties.isUseJsonFormat()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Your request cannot be processed 8 | 74 | 75 | 76 |

77 |

Your request cannot be processed :(

78 | 79 |

Sorry, an error has occurred.

80 | 81 | Status:  ()
82 | 83 | Message: 
84 |
85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Project Specific 3 | ###################### 4 | /target/classes/static/** 5 | /src/test/javascript/coverage/ 6 | 7 | ###################### 8 | # Node 9 | ###################### 10 | /node/ 11 | node_tmp/ 12 | node_modules/ 13 | npm-debug.log.* 14 | /.awcache/* 15 | /.cache-loader/* 16 | 17 | ###################### 18 | # SASS 19 | ###################### 20 | .sass-cache/ 21 | 22 | ###################### 23 | # Eclipse 24 | ###################### 25 | *.pydevproject 26 | .project 27 | .metadata 28 | tmp/ 29 | tmp/**/* 30 | *.tmp 31 | *.bak 32 | *.swp 33 | *~.nib 34 | local.properties 35 | .classpath 36 | .settings/ 37 | .loadpath 38 | .factorypath 39 | /src/main/resources/rebel.xml 40 | 41 | # External tool builders 42 | .externalToolBuilders/** 43 | 44 | # Locally stored "Eclipse launch configurations" 45 | *.launch 46 | 47 | # CDT-specific 48 | .cproject 49 | 50 | # PDT-specific 51 | .buildpath 52 | 53 | # STS-specific 54 | /.sts4-cache/* 55 | ###################### 56 | # Intellij 57 | ###################### 58 | .idea/ 59 | *.iml 60 | *.iws 61 | *.ipr 62 | *.ids 63 | *.orig 64 | classes/ 65 | out/ 66 | 67 | ###################### 68 | # Visual Studio Code 69 | ###################### 70 | .vscode/ 71 | 72 | ###################### 73 | # Maven 74 | ###################### 75 | /log/ 76 | /target/ 77 | 78 | ###################### 79 | # Gradle 80 | ###################### 81 | .gradle/ 82 | /build/ 83 | 84 | ###################### 85 | # Package Files 86 | ###################### 87 | *.jar 88 | *.war 89 | *.ear 90 | *.db 91 | 92 | ###################### 93 | # Windows 94 | ###################### 95 | # Windows image file caches 96 | Thumbs.db 97 | 98 | # Folder config file 99 | Desktop.ini 100 | 101 | ###################### 102 | # Mac OSX 103 | ###################### 104 | .DS_Store 105 | .svn 106 | 107 | # Thumbnails 108 | ._* 109 | 110 | # Files that might appear on external disk 111 | .Spotlight-V100 112 | .Trashes 113 | 114 | ###################### 115 | # Directories 116 | ###################### 117 | /bin/ 118 | /deploy/ 119 | 120 | ###################### 121 | # Logs 122 | ###################### 123 | *.log* 124 | 125 | ###################### 126 | # Others 127 | ###################### 128 | *.class 129 | *.*~ 130 | *~ 131 | .merge_file* 132 | 133 | ###################### 134 | # Gradle Wrapper 135 | ###################### 136 | !gradle/wrapper/gradle-wrapper.jar 137 | 138 | ###################### 139 | # Maven Wrapper 140 | ###################### 141 | !.mvn/wrapper/maven-wrapper.jar 142 | 143 | ###################### 144 | # ESLint 145 | ###################### 146 | .eslintcache 147 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/WebConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import io.github.jhipster.config.JHipsterConstants; 4 | import io.github.jhipster.config.JHipsterProperties; 5 | import io.github.jhipster.config.h2.H2ConfigurationHelper; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.boot.web.server.*; 9 | import org.springframework.boot.web.servlet.ServletContextInitializer; 10 | import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.core.env.Environment; 14 | import org.springframework.core.env.Profiles; 15 | import org.springframework.web.cors.CorsConfiguration; 16 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 17 | import org.springframework.web.filter.CorsFilter; 18 | 19 | import javax.servlet.*; 20 | 21 | /** 22 | * Configuration of web application with Servlet 3.0 APIs. 23 | */ 24 | @Configuration 25 | public class WebConfigurer implements ServletContextInitializer { 26 | 27 | private final Logger log = LoggerFactory.getLogger(WebConfigurer.class); 28 | 29 | private final Environment env; 30 | 31 | private final JHipsterProperties jHipsterProperties; 32 | 33 | public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) { 34 | this.env = env; 35 | this.jHipsterProperties = jHipsterProperties; 36 | } 37 | 38 | @Override 39 | public void onStartup(ServletContext servletContext) throws ServletException { 40 | if (env.getActiveProfiles().length != 0) { 41 | log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles()); 42 | } 43 | if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { 44 | initH2Console(servletContext); 45 | } 46 | log.info("Web application fully configured"); 47 | } 48 | 49 | @Bean 50 | public CorsFilter corsFilter() { 51 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 52 | CorsConfiguration config = jHipsterProperties.getCors(); 53 | if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) { 54 | log.debug("Registering CORS filter"); 55 | source.registerCorsConfiguration("/api/**", config); 56 | source.registerCorsConfiguration("/management/**", config); 57 | source.registerCorsConfiguration("/v2/api-docs", config); 58 | } 59 | return new CorsFilter(source); 60 | } 61 | 62 | /** 63 | * Initializes H2 console. 64 | */ 65 | private void initH2Console(ServletContext servletContext) { 66 | log.debug("Initialize H2 console"); 67 | H2ConfigurationHelper.initH2Console(servletContext); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/PersistentAuditEvent.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import javax.persistence.*; 4 | import javax.validation.constraints.NotNull; 5 | import java.io.Serializable; 6 | import java.time.Instant; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Persist AuditEvent managed by the Spring Boot actuator. 12 | * 13 | * @see org.springframework.boot.actuate.audit.AuditEvent 14 | */ 15 | @Entity 16 | @Table(name = "jhi_persistent_audit_event") 17 | public class PersistentAuditEvent implements Serializable { 18 | 19 | private static final long serialVersionUID = 1L; 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | @Column(name = "event_id") 24 | private Long id; 25 | 26 | @NotNull 27 | @Column(nullable = false) 28 | private String principal; 29 | 30 | @Column(name = "event_date") 31 | private Instant auditEventDate; 32 | 33 | @Column(name = "event_type") 34 | private String auditEventType; 35 | 36 | @ElementCollection 37 | @MapKeyColumn(name = "name") 38 | @Column(name = "value") 39 | @CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id")) 40 | private Map data = new HashMap<>(); 41 | 42 | public Long getId() { 43 | return id; 44 | } 45 | 46 | public void setId(Long id) { 47 | this.id = id; 48 | } 49 | 50 | public String getPrincipal() { 51 | return principal; 52 | } 53 | 54 | public void setPrincipal(String principal) { 55 | this.principal = principal; 56 | } 57 | 58 | public Instant getAuditEventDate() { 59 | return auditEventDate; 60 | } 61 | 62 | public void setAuditEventDate(Instant auditEventDate) { 63 | this.auditEventDate = auditEventDate; 64 | } 65 | 66 | public String getAuditEventType() { 67 | return auditEventType; 68 | } 69 | 70 | public void setAuditEventType(String auditEventType) { 71 | this.auditEventType = auditEventType; 72 | } 73 | 74 | public Map getData() { 75 | return data; 76 | } 77 | 78 | public void setData(Map data) { 79 | this.data = data; 80 | } 81 | 82 | @Override 83 | public boolean equals(Object o) { 84 | if (this == o) { 85 | return true; 86 | } 87 | if (!(o instanceof PersistentAuditEvent)) { 88 | return false; 89 | } 90 | return id != null && id.equals(((PersistentAuditEvent) o).id); 91 | } 92 | 93 | @Override 94 | public int hashCode() { 95 | return 31; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return "PersistentAuditEvent{" + 101 | "principal='" + principal + '\'' + 102 | ", auditEventDate=" + auditEventDate + 103 | ", auditEventType='" + auditEventType + '\'' + 104 | '}'; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/service/impl/InStockBookServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.service.impl; 2 | 3 | import com.skcc.book.service.InStockBookService; 4 | import com.skcc.book.domain.InStockBook; 5 | import com.skcc.book.repository.InStockBookRepository; 6 | import com.skcc.book.web.rest.dto.InStockBookDTO; 7 | import com.skcc.book.web.rest.mapper.InStockBookMapper; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import org.springframework.data.domain.Page; 12 | import org.springframework.data.domain.Pageable; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.Optional; 17 | 18 | /** 19 | * Service Implementation for managing {@link InStockBook}. 20 | */ 21 | @Service 22 | @Transactional 23 | public class InStockBookServiceImpl implements InStockBookService { 24 | 25 | private final Logger log = LoggerFactory.getLogger(InStockBookServiceImpl.class); 26 | 27 | private final InStockBookRepository inStockBookRepository; 28 | 29 | private final InStockBookMapper inStockBookMapper; 30 | 31 | public InStockBookServiceImpl(InStockBookRepository inStockBookRepository, InStockBookMapper inStockBookMapper) { 32 | this.inStockBookRepository = inStockBookRepository; 33 | this.inStockBookMapper = inStockBookMapper; 34 | } 35 | 36 | /** 37 | * Save a inStockBook. 38 | * 39 | * @param inStockBook the entity to save. 40 | * @return the persisted entity. 41 | */ 42 | @Override 43 | public InStockBook save(InStockBook inStockBook) { 44 | log.debug("Request to save InStockBook : {}", inStockBook); 45 | return inStockBookRepository.save(inStockBook); 46 | } 47 | 48 | /** 49 | * Get all the inStockBooks. 50 | * 51 | * @param pageable the pagination information. 52 | * @return the list of entities. 53 | */ 54 | @Override 55 | @Transactional(readOnly = true) 56 | public Page findAll(Pageable pageable) { 57 | log.debug("Request to get all InStockBooks"); 58 | return inStockBookRepository.findAll(pageable); 59 | } 60 | 61 | /** 62 | * Get one inStockBook by id. 63 | * 64 | * @param id the id of the entity. 65 | * @return the entity. 66 | */ 67 | @Override 68 | @Transactional(readOnly = true) 69 | public InStockBook findOne(Long id) { 70 | log.debug("Request to get InStockBook : {}", id); 71 | return inStockBookRepository.findById(id).get(); 72 | } 73 | 74 | /** 75 | * Delete the inStockBook by id. 76 | * 77 | * @param id the id of the entity. 78 | */ 79 | @Override 80 | public void delete(Long id) { 81 | log.debug("Request to delete InStockBook : {}", id); 82 | inStockBookRepository.deleteById(id); 83 | } 84 | 85 | @Override 86 | public Page findByTitle(String title, Pageable pageable) { 87 | return inStockBookRepository.findByTitleContaining(title, pageable).map(inStockBookMapper::toDto); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/InStockBook.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.ToString; 6 | import org.hibernate.annotations.Cache; 7 | import org.hibernate.annotations.CacheConcurrencyStrategy; 8 | 9 | import javax.persistence.*; 10 | 11 | import java.io.Serializable; 12 | import java.util.Objects; 13 | import java.time.LocalDate; 14 | 15 | import com.skcc.book.domain.enumeration.Source; 16 | 17 | /** 18 | * A InStockBook. 19 | */ 20 | @Entity 21 | @Table(name = "in_stock_book") 22 | @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) 23 | @Data 24 | @ToString 25 | public class InStockBook implements Serializable { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | @Id 30 | @GeneratedValue(strategy = GenerationType.IDENTITY) 31 | private Long id; 32 | 33 | @Column(name = "title") 34 | private String title; 35 | 36 | @Column(name = "description") 37 | private String description; 38 | 39 | @Column(name = "author") 40 | private String author; 41 | 42 | @Column(name = "publisher") 43 | private String publisher; 44 | 45 | @Column(name = "isbn") 46 | private Long isbn; 47 | 48 | @Column(name = "publication_date") 49 | private LocalDate publicationDate; 50 | 51 | @Enumerated(EnumType.STRING) 52 | @Column(name = "source") 53 | private Source source; 54 | 55 | // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove 56 | 57 | public InStockBook title(String title) { 58 | this.title = title; 59 | return this; 60 | } 61 | 62 | 63 | public InStockBook description(String description) { 64 | this.description = description; 65 | return this; 66 | } 67 | 68 | 69 | public InStockBook author(String author) { 70 | this.author = author; 71 | return this; 72 | } 73 | 74 | 75 | public InStockBook publisher(String publisher) { 76 | this.publisher = publisher; 77 | return this; 78 | } 79 | 80 | 81 | public InStockBook isbn(Long isbn) { 82 | this.isbn = isbn; 83 | return this; 84 | } 85 | 86 | 87 | public InStockBook publicationDate(LocalDate publicationDate) { 88 | this.publicationDate = publicationDate; 89 | return this; 90 | } 91 | 92 | 93 | public InStockBook source(Source source) { 94 | this.source = source; 95 | return this; 96 | } 97 | 98 | // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove 99 | 100 | @Override 101 | public boolean equals(Object o) { 102 | if (this == o) { 103 | return true; 104 | } 105 | if (!(o instanceof InStockBook)) { 106 | return false; 107 | } 108 | return id != null && id.equals(((InStockBook) o).id); 109 | } 110 | 111 | @Override 112 | public int hashCode() { 113 | return 31; 114 | } 115 | 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/LiquibaseConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import io.github.jhipster.config.JHipsterConstants; 4 | import io.github.jhipster.config.liquibase.SpringLiquibaseUtil; 5 | import liquibase.integration.spring.SpringLiquibase; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.ObjectProvider; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 11 | import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource; 12 | import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.core.env.Environment; 16 | import org.springframework.core.env.Profiles; 17 | 18 | import javax.sql.DataSource; 19 | import java.util.concurrent.Executor; 20 | 21 | @Configuration 22 | public class LiquibaseConfiguration { 23 | 24 | private final Logger log = LoggerFactory.getLogger(LiquibaseConfiguration.class); 25 | 26 | private final Environment env; 27 | 28 | public LiquibaseConfiguration(Environment env) { 29 | this.env = env; 30 | } 31 | 32 | @Bean 33 | public SpringLiquibase liquibase(@Qualifier("taskExecutor") Executor executor, 34 | @LiquibaseDataSource ObjectProvider liquibaseDataSource, LiquibaseProperties liquibaseProperties, 35 | ObjectProvider dataSource, DataSourceProperties dataSourceProperties) { 36 | 37 | // If you don't want Liquibase to start asynchronously, substitute by this: 38 | // SpringLiquibase liquibase = SpringLiquibaseUtil.createSpringLiquibase(liquibaseDataSource.getIfAvailable(), liquibaseProperties, dataSource.getIfUnique(), dataSourceProperties); 39 | SpringLiquibase liquibase = SpringLiquibaseUtil.createAsyncSpringLiquibase(this.env, executor, liquibaseDataSource.getIfAvailable(), liquibaseProperties, dataSource.getIfUnique(), dataSourceProperties); 40 | liquibase.setChangeLog("classpath:config/liquibase/master.xml"); 41 | liquibase.setContexts(liquibaseProperties.getContexts()); 42 | liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); 43 | liquibase.setLiquibaseSchema(liquibaseProperties.getLiquibaseSchema()); 44 | liquibase.setLiquibaseTablespace(liquibaseProperties.getLiquibaseTablespace()); 45 | liquibase.setDatabaseChangeLogLockTable(liquibaseProperties.getDatabaseChangeLogLockTable()); 46 | liquibase.setDatabaseChangeLogTable(liquibaseProperties.getDatabaseChangeLogTable()); 47 | liquibase.setDropFirst(liquibaseProperties.isDropFirst()); 48 | liquibase.setLabels(liquibaseProperties.getLabels()); 49 | liquibase.setChangeLogParameters(liquibaseProperties.getParameters()); 50 | liquibase.setRollbackFile(liquibaseProperties.getRollbackFile()); 51 | liquibase.setTestRollbackOnUpdate(liquibaseProperties.isTestRollbackOnUpdate()); 52 | if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE))) { 53 | liquibase.setShouldRun(false); 54 | } else { 55 | liquibase.setShouldRun(liquibaseProperties.isEnabled()); 56 | log.debug("Configuring Liquibase"); 57 | } 58 | return liquibase; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/dto/InStockBookDTO.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.dto; 2 | 3 | import java.time.LocalDate; 4 | import java.io.Serializable; 5 | import java.util.Objects; 6 | import com.skcc.book.domain.enumeration.Source; 7 | 8 | /** 9 | * A DTO for the {@link com.skcc.book.domain.InStockBook} entity. 10 | */ 11 | public class InStockBookDTO implements Serializable { 12 | 13 | private Long id; 14 | 15 | private String title; 16 | 17 | private String description; 18 | 19 | private String author; 20 | 21 | private String publisher; 22 | 23 | private Long isbn; 24 | 25 | private LocalDate publicationDate; 26 | 27 | private Source source; 28 | 29 | 30 | public Long getId() { 31 | return id; 32 | } 33 | 34 | public void setId(Long id) { 35 | this.id = id; 36 | } 37 | 38 | public String getTitle() { 39 | return title; 40 | } 41 | 42 | public void setTitle(String title) { 43 | this.title = title; 44 | } 45 | 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | public void setDescription(String description) { 51 | this.description = description; 52 | } 53 | 54 | public String getAuthor() { 55 | return author; 56 | } 57 | 58 | public void setAuthor(String author) { 59 | this.author = author; 60 | } 61 | 62 | public String getPublisher() { 63 | return publisher; 64 | } 65 | 66 | public void setPublisher(String publisher) { 67 | this.publisher = publisher; 68 | } 69 | 70 | public Long getIsbn() { 71 | return isbn; 72 | } 73 | 74 | public void setIsbn(Long isbn) { 75 | this.isbn = isbn; 76 | } 77 | 78 | public LocalDate getPublicationDate() { 79 | return publicationDate; 80 | } 81 | 82 | public void setPublicationDate(LocalDate publicationDate) { 83 | this.publicationDate = publicationDate; 84 | } 85 | 86 | public Source getSource() { 87 | return source; 88 | } 89 | 90 | public void setSource(Source source) { 91 | this.source = source; 92 | } 93 | 94 | @Override 95 | public boolean equals(Object o) { 96 | if (this == o) { 97 | return true; 98 | } 99 | if (o == null || getClass() != o.getClass()) { 100 | return false; 101 | } 102 | 103 | InStockBookDTO inStockBookDTO = (InStockBookDTO) o; 104 | if (inStockBookDTO.getId() == null || getId() == null) { 105 | return false; 106 | } 107 | return Objects.equals(getId(), inStockBookDTO.getId()); 108 | } 109 | 110 | @Override 111 | public int hashCode() { 112 | return Objects.hashCode(getId()); 113 | } 114 | 115 | @Override 116 | public String toString() { 117 | return "InStockBookDTO{" + 118 | "id=" + getId() + 119 | ", title='" + getTitle() + "'" + 120 | ", description='" + getDescription() + "'" + 121 | ", author='" + getAuthor() + "'" + 122 | ", publisher='" + getPublisher() + "'" + 123 | ", isbn=" + getIsbn() + 124 | ", publicationDate='" + getPublicationDate() + "'" + 125 | ", source='" + getSource() + "'" + 126 | "}"; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.context.SecurityContext; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | 9 | import java.util.Optional; 10 | import java.util.stream.Stream; 11 | 12 | /** 13 | * Utility class for Spring Security. 14 | */ 15 | public final class SecurityUtils { 16 | 17 | private SecurityUtils() { 18 | } 19 | 20 | /** 21 | * Get the login of the current user. 22 | * 23 | * @return the login of the current user. 24 | */ 25 | public static Optional getCurrentUserLogin() { 26 | SecurityContext securityContext = SecurityContextHolder.getContext(); 27 | return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication())); 28 | } 29 | 30 | private static String extractPrincipal(Authentication authentication) { 31 | if (authentication == null) { 32 | return null; 33 | } else if (authentication.getPrincipal() instanceof UserDetails) { 34 | UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); 35 | return springSecurityUser.getUsername(); 36 | } else if (authentication.getPrincipal() instanceof String) { 37 | return (String) authentication.getPrincipal(); 38 | } 39 | return null; 40 | } 41 | 42 | 43 | /** 44 | * Get the JWT of the current user. 45 | * 46 | * @return the JWT of the current user. 47 | */ 48 | public static Optional getCurrentUserJWT() { 49 | SecurityContext securityContext = SecurityContextHolder.getContext(); 50 | return Optional.ofNullable(securityContext.getAuthentication()) 51 | .filter(authentication -> authentication.getCredentials() instanceof String) 52 | .map(authentication -> (String) authentication.getCredentials()); 53 | } 54 | 55 | /** 56 | * Check if a user is authenticated. 57 | * 58 | * @return true if the user is authenticated, false otherwise. 59 | */ 60 | public static boolean isAuthenticated() { 61 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 62 | return authentication != null && 63 | getAuthorities(authentication).noneMatch(AuthoritiesConstants.ANONYMOUS::equals); 64 | } 65 | 66 | /** 67 | * If the current user has a specific authority (security role). 68 | *

69 | * The name of this method comes from the {@code isUserInRole()} method in the Servlet API. 70 | * 71 | * @param authority the authority to check. 72 | * @return true if the current user has the authority, false otherwise. 73 | */ 74 | public static boolean isCurrentUserInRole(String authority) { 75 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 76 | return authentication != null && 77 | getAuthorities(authentication).anyMatch(authority::equals); 78 | } 79 | 80 | private static Stream getAuthorities(Authentication authentication) { 81 | return authentication.getAuthorities().stream() 82 | .map(GrantedAuthority::getAuthority); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/adaptor/BookConsumer.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.adaptor; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.skcc.book.config.KafkaProperties; 5 | import com.skcc.book.domain.Book; 6 | import com.skcc.book.domain.enumeration.BookStatus; 7 | import com.skcc.book.domain.event.StockChanged; 8 | import com.skcc.book.service.BookService; 9 | import org.apache.kafka.clients.consumer.ConsumerRecord; 10 | import org.apache.kafka.clients.consumer.ConsumerRecords; 11 | import org.apache.kafka.clients.consumer.KafkaConsumer; 12 | import org.apache.kafka.common.errors.WakeupException; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.stereotype.Service; 16 | 17 | import javax.annotation.PostConstruct; 18 | import java.time.Duration; 19 | import java.util.Collections; 20 | import java.util.concurrent.ExecutorService; 21 | import java.util.concurrent.Executors; 22 | import java.util.concurrent.atomic.AtomicBoolean; 23 | 24 | @Service 25 | public class BookConsumer { 26 | 27 | private final Logger log = LoggerFactory.getLogger(BookConsumer.class); 28 | private final AtomicBoolean closed = new AtomicBoolean(false); 29 | public static final String TOPIC ="topic_book"; 30 | private final KafkaProperties kafkaProperties; 31 | private KafkaConsumer kafkaConsumer; 32 | private BookService bookService; 33 | private ExecutorService executorService = Executors.newCachedThreadPool(); 34 | 35 | public BookConsumer(KafkaProperties kafkaProperties, BookService bookService) { 36 | this.kafkaProperties = kafkaProperties; 37 | this.bookService = bookService; 38 | } 39 | 40 | @PostConstruct 41 | public void start(){ 42 | log.info("Kafka consumer starting ..."); 43 | this.kafkaConsumer = new KafkaConsumer<>(kafkaProperties.getConsumerProps()); 44 | Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); 45 | kafkaConsumer.subscribe(Collections.singleton(TOPIC)); 46 | log.info("Kafka consumer started"); 47 | 48 | executorService.execute(()-> { 49 | try { 50 | while (!closed.get()){ 51 | ConsumerRecords records = kafkaConsumer.poll(Duration.ofSeconds(3)); 52 | for(ConsumerRecord record: records){ 53 | log.info("Consumed message in {} : {}", TOPIC, record.value()); 54 | ObjectMapper objectMapper = new ObjectMapper(); 55 | StockChanged stockChanged = objectMapper.readValue(record.value(), StockChanged.class); 56 | bookService.processChangeBookState(stockChanged.getBookId(), stockChanged.getBookStatus()); 57 | } 58 | } 59 | kafkaConsumer.commitSync(); 60 | }catch (WakeupException e){ 61 | if(!closed.get()){ 62 | throw e; 63 | } 64 | }catch (Exception e){ 65 | log.error(e.getMessage(), e); 66 | }finally { 67 | log.info("kafka consumer close"); 68 | kafkaConsumer.close(); 69 | } 70 | } 71 | ); 72 | } 73 | 74 | public KafkaConsumer getKafkaConsumer() { 75 | return kafkaConsumer; 76 | } 77 | public void shutdown() { 78 | log.info("Shutdown Kafka consumer"); 79 | closed.set(true); 80 | kafkaConsumer.wakeup(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/security/SecurityUtilsUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security; 2 | 3 | import org.assertj.core.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.context.SecurityContext; 9 | import org.springframework.security.core.context.SecurityContextHolder; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.Optional; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | /** 18 | * Test class for the {@link SecurityUtils} utility class. 19 | */ 20 | public class SecurityUtilsUnitTest { 21 | 22 | @Test 23 | public void testGetCurrentUserLogin() { 24 | SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); 25 | securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); 26 | SecurityContextHolder.setContext(securityContext); 27 | Optional login = SecurityUtils.getCurrentUserLogin(); 28 | assertThat(login).contains("admin"); 29 | } 30 | 31 | @Test 32 | public void testgetCurrentUserJWT() { 33 | SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); 34 | securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "token")); 35 | SecurityContextHolder.setContext(securityContext); 36 | Optional jwt = SecurityUtils.getCurrentUserJWT(); 37 | assertThat(jwt).contains("token"); 38 | } 39 | 40 | @Test 41 | public void testIsAuthenticated() { 42 | SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); 43 | securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); 44 | SecurityContextHolder.setContext(securityContext); 45 | boolean isAuthenticated = SecurityUtils.isAuthenticated(); 46 | assertThat(isAuthenticated).isTrue(); 47 | } 48 | 49 | @Test 50 | public void testAnonymousIsNotAuthenticated() { 51 | SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); 52 | Collection authorities = new ArrayList<>(); 53 | authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); 54 | securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities)); 55 | SecurityContextHolder.setContext(securityContext); 56 | boolean isAuthenticated = SecurityUtils.isAuthenticated(); 57 | assertThat(isAuthenticated).isFalse(); 58 | } 59 | 60 | @Test 61 | public void testIsCurrentUserInRole() { 62 | SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); 63 | Collection authorities = new ArrayList<>(); 64 | authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER)); 65 | securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("user", "user", authorities)); 66 | SecurityContextHolder.setContext(securityContext); 67 | 68 | assertThat(SecurityUtils.isCurrentUserInRole(AuthoritiesConstants.USER)).isTrue(); 69 | assertThat(SecurityUtils.isCurrentUserInRole(AuthoritiesConstants.ADMIN)).isFalse(); 70 | } 71 | 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config; 2 | 3 | import com.skcc.book.security.*; 4 | import com.skcc.book.security.jwt.*; 5 | 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Import; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 13 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 14 | import org.springframework.security.config.http.SessionCreationPolicy; 15 | import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter; 16 | import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport; 17 | 18 | @EnableWebSecurity 19 | @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) 20 | @Import(SecurityProblemSupport.class) 21 | public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 22 | 23 | private final TokenProvider tokenProvider; 24 | private final SecurityProblemSupport problemSupport; 25 | 26 | public SecurityConfiguration(TokenProvider tokenProvider, SecurityProblemSupport problemSupport) { 27 | this.tokenProvider = tokenProvider; 28 | this.problemSupport = problemSupport; 29 | } 30 | @Override 31 | public void configure(WebSecurity web) { 32 | web.ignoring() 33 | .antMatchers("/h2-console/**"); 34 | } 35 | 36 | @Override 37 | public void configure(HttpSecurity http) throws Exception { 38 | // @formatter:off 39 | http 40 | .csrf() 41 | .disable() 42 | .exceptionHandling() 43 | .authenticationEntryPoint(problemSupport) 44 | .accessDeniedHandler(problemSupport) 45 | .and() 46 | .headers() 47 | .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:") 48 | .and() 49 | .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) 50 | .and() 51 | .featurePolicy("geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'self'; payment 'none'") 52 | .and() 53 | .frameOptions() 54 | .deny() 55 | .and() 56 | .sessionManagement() 57 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 58 | .and() 59 | .authorizeRequests() 60 | .antMatchers("/api/authenticate").permitAll() 61 | .antMatchers("/api/**").permitAll() 62 | .antMatchers("/management/health").permitAll() 63 | .antMatchers("/management/info").permitAll() 64 | .antMatchers("/management/prometheus").permitAll() 65 | .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) 66 | .and() 67 | .apply(securityConfigurerAdapter()); 68 | // @formatter:on 69 | } 70 | 71 | private JWTConfigurer securityConfigurerAdapter() { 72 | return new JWTConfigurer(tokenProvider); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/config/audit/AuditEventConverter.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.config.audit; 2 | 3 | import com.skcc.book.domain.PersistentAuditEvent; 4 | 5 | import org.springframework.boot.actuate.audit.AuditEvent; 6 | import org.springframework.security.web.authentication.WebAuthenticationDetails; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.*; 10 | 11 | @Component 12 | public class AuditEventConverter { 13 | 14 | /** 15 | * Convert a list of {@link PersistentAuditEvent}s to a list of {@link AuditEvent}s. 16 | * 17 | * @param persistentAuditEvents the list to convert. 18 | * @return the converted list. 19 | */ 20 | public List convertToAuditEvent(Iterable persistentAuditEvents) { 21 | if (persistentAuditEvents == null) { 22 | return Collections.emptyList(); 23 | } 24 | List auditEvents = new ArrayList<>(); 25 | for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) { 26 | auditEvents.add(convertToAuditEvent(persistentAuditEvent)); 27 | } 28 | return auditEvents; 29 | } 30 | 31 | /** 32 | * Convert a {@link PersistentAuditEvent} to an {@link AuditEvent}. 33 | * 34 | * @param persistentAuditEvent the event to convert. 35 | * @return the converted list. 36 | */ 37 | public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) { 38 | if (persistentAuditEvent == null) { 39 | return null; 40 | } 41 | return new AuditEvent(persistentAuditEvent.getAuditEventDate(), persistentAuditEvent.getPrincipal(), 42 | persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData())); 43 | } 44 | 45 | /** 46 | * Internal conversion. This is needed to support the current SpringBoot actuator {@code AuditEventRepository} interface. 47 | * 48 | * @param data the data to convert. 49 | * @return a map of {@link String}, {@link Object}. 50 | */ 51 | public Map convertDataToObjects(Map data) { 52 | Map results = new HashMap<>(); 53 | 54 | if (data != null) { 55 | for (Map.Entry entry : data.entrySet()) { 56 | results.put(entry.getKey(), entry.getValue()); 57 | } 58 | } 59 | return results; 60 | } 61 | 62 | /** 63 | * Internal conversion. This method will allow to save additional data. 64 | * By default, it will save the object as string. 65 | * 66 | * @param data the data to convert. 67 | * @return a map of {@link String}, {@link String}. 68 | */ 69 | public Map convertDataToStrings(Map data) { 70 | Map results = new HashMap<>(); 71 | 72 | if (data != null) { 73 | for (Map.Entry entry : data.entrySet()) { 74 | // Extract the data that will be saved. 75 | if (entry.getValue() instanceof WebAuthenticationDetails) { 76 | WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) entry.getValue(); 77 | results.put("remoteAddress", authenticationDetails.getRemoteAddress()); 78 | results.put("sessionId", authenticationDetails.getSessionId()); 79 | } else { 80 | results.put(entry.getKey(), Objects.toString(entry.getValue())); 81 | } 82 | } 83 | } 84 | return results; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/changelog/20200610085200_added_entity_InStockBook.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 57 | 58 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/repository/timezone/DateTimeWrapper.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.repository.timezone; 2 | 3 | import javax.persistence.*; 4 | import java.io.Serializable; 5 | import java.time.*; 6 | import java.util.Objects; 7 | 8 | @Entity 9 | @Table(name = "jhi_date_time_wrapper") 10 | public class DateTimeWrapper implements Serializable { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | @Column(name = "instant") 19 | private Instant instant; 20 | 21 | @Column(name = "local_date_time") 22 | private LocalDateTime localDateTime; 23 | 24 | @Column(name = "offset_date_time") 25 | private OffsetDateTime offsetDateTime; 26 | 27 | @Column(name = "zoned_date_time") 28 | private ZonedDateTime zonedDateTime; 29 | 30 | @Column(name = "local_time") 31 | private LocalTime localTime; 32 | 33 | @Column(name = "offset_time") 34 | private OffsetTime offsetTime; 35 | 36 | @Column(name = "local_date") 37 | private LocalDate localDate; 38 | 39 | public Long getId() { 40 | return id; 41 | } 42 | 43 | public void setId(Long id) { 44 | this.id = id; 45 | } 46 | 47 | public Instant getInstant() { 48 | return instant; 49 | } 50 | 51 | public void setInstant(Instant instant) { 52 | this.instant = instant; 53 | } 54 | 55 | public LocalDateTime getLocalDateTime() { 56 | return localDateTime; 57 | } 58 | 59 | public void setLocalDateTime(LocalDateTime localDateTime) { 60 | this.localDateTime = localDateTime; 61 | } 62 | 63 | public OffsetDateTime getOffsetDateTime() { 64 | return offsetDateTime; 65 | } 66 | 67 | public void setOffsetDateTime(OffsetDateTime offsetDateTime) { 68 | this.offsetDateTime = offsetDateTime; 69 | } 70 | 71 | public ZonedDateTime getZonedDateTime() { 72 | return zonedDateTime; 73 | } 74 | 75 | public void setZonedDateTime(ZonedDateTime zonedDateTime) { 76 | this.zonedDateTime = zonedDateTime; 77 | } 78 | 79 | public LocalTime getLocalTime() { 80 | return localTime; 81 | } 82 | 83 | public void setLocalTime(LocalTime localTime) { 84 | this.localTime = localTime; 85 | } 86 | 87 | public OffsetTime getOffsetTime() { 88 | return offsetTime; 89 | } 90 | 91 | public void setOffsetTime(OffsetTime offsetTime) { 92 | this.offsetTime = offsetTime; 93 | } 94 | 95 | public LocalDate getLocalDate() { 96 | return localDate; 97 | } 98 | 99 | public void setLocalDate(LocalDate localDate) { 100 | this.localDate = localDate; 101 | } 102 | 103 | @Override 104 | public boolean equals(Object o) { 105 | if (this == o) { 106 | return true; 107 | } 108 | if (o == null || getClass() != o.getClass()) { 109 | return false; 110 | } 111 | 112 | DateTimeWrapper dateTimeWrapper = (DateTimeWrapper) o; 113 | return !(dateTimeWrapper.getId() == null || getId() == null) && Objects.equals(getId(), dateTimeWrapper.getId()); 114 | } 115 | 116 | @Override 117 | public int hashCode() { 118 | return Objects.hashCode(getId()); 119 | } 120 | 121 | @Override 122 | public String toString() { 123 | return "TimeZoneTest{" + 124 | "id=" + id + 125 | ", instant=" + instant + 126 | ", localDateTime=" + localDateTime + 127 | ", offsetDateTime=" + offsetDateTime + 128 | ", zonedDateTime=" + zonedDateTime + 129 | '}'; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/domain/Book.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.domain; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.ToString; 6 | import org.hibernate.annotations.Cache; 7 | import org.hibernate.annotations.CacheConcurrencyStrategy; 8 | 9 | import javax.persistence.*; 10 | 11 | import java.io.Serializable; 12 | import java.util.Comparator; 13 | import java.util.HashSet; 14 | import java.time.LocalDate; 15 | import java.util.Objects; 16 | import java.util.Set; 17 | 18 | import com.skcc.book.domain.enumeration.Classification; 19 | 20 | import com.skcc.book.domain.enumeration.BookStatus; 21 | 22 | import com.skcc.book.domain.enumeration.Location; 23 | import org.hibernate.annotations.Cascade; 24 | 25 | /** 26 | * A Book. 27 | */ 28 | 29 | @Entity 30 | @Table(name = "book") 31 | @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) 32 | @Data 33 | @ToString 34 | public class Book implements Serializable { 35 | 36 | private static final long serialVersionUID = 1L; 37 | 38 | @Id 39 | @GeneratedValue(strategy = GenerationType.IDENTITY) 40 | private Long id; 41 | 42 | @Column(name = "title") 43 | private String title; 44 | 45 | @Column(name = "description") 46 | private String description; 47 | 48 | @Column(name = "author") 49 | private String author; 50 | 51 | @Column(name = "publisher") 52 | private String publisher; 53 | 54 | @Column(name = "isbn") 55 | private Long isbn; 56 | 57 | @Column(name = "publication_date") 58 | private LocalDate publicationDate; 59 | 60 | @Enumerated(EnumType.STRING) 61 | @Column(name = "classification") 62 | private Classification classification; 63 | 64 | @Enumerated(EnumType.STRING) 65 | @Column(name = "book_status") 66 | private BookStatus bookStatus; 67 | 68 | @Enumerated(EnumType.STRING) 69 | @Column(name = "location") 70 | private Location location; 71 | 72 | // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove 73 | 74 | public Book title(String title) { 75 | this.title = title; 76 | return this; 77 | } 78 | 79 | public Book description(String description) { 80 | this.description = description; 81 | return this; 82 | } 83 | 84 | 85 | public Book author(String author) { 86 | this.author = author; 87 | return this; 88 | } 89 | 90 | 91 | public Book publisher(String publisher) { 92 | this.publisher = publisher; 93 | return this; 94 | } 95 | 96 | 97 | public Book isbn(Long isbn) { 98 | this.isbn = isbn; 99 | return this; 100 | } 101 | 102 | 103 | public Book publicationDate(LocalDate publicationDate) { 104 | this.publicationDate = publicationDate; 105 | return this; 106 | } 107 | 108 | 109 | public Book classification(Classification classification) { 110 | this.classification = classification; 111 | return this; 112 | } 113 | 114 | 115 | public Book bookStatus(BookStatus bookStatus) { 116 | this.bookStatus = bookStatus; 117 | return this; 118 | } 119 | 120 | 121 | public Book location(Location location) { 122 | this.location = location; 123 | return this; 124 | } 125 | 126 | // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove 127 | 128 | @Override 129 | public boolean equals(Object o) { 130 | if (this == o) { 131 | return true; 132 | } 133 | if (!(o instanceof Book)) { 134 | return false; 135 | } 136 | return id != null && id.equals(((Book) o).id); 137 | } 138 | 139 | @Override 140 | public int hashCode() { 141 | return 31; 142 | } 143 | 144 | 145 | 146 | 147 | } 148 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # This file is inspired by https://github.com/alexkaratarakis/gitattributes 2 | # 3 | # Auto detect text files and perform LF normalization 4 | # http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ 5 | * text=auto 6 | 7 | # The above will handle all files NOT found below 8 | # These files are text and should be normalized (Convert crlf => lf) 9 | 10 | *.bat text eol=crlf 11 | *.cmd text eol=crlf 12 | *.ps1 text eol=crlf 13 | *.coffee text 14 | *.css text 15 | *.cql text 16 | *.df text 17 | *.ejs text 18 | *.html text 19 | *.java text 20 | *.js text 21 | *.json text 22 | *.less text 23 | *.properties text 24 | *.sass text 25 | *.scss text 26 | *.sh text eol=lf 27 | *.sql text 28 | *.txt text 29 | *.ts text 30 | *.xml text 31 | *.yaml text 32 | *.yml text 33 | 34 | # Documents 35 | *.doc diff=astextplain 36 | *.DOC diff=astextplain 37 | *.docx diff=astextplain 38 | *.DOCX diff=astextplain 39 | *.dot diff=astextplain 40 | *.DOT diff=astextplain 41 | *.pdf diff=astextplain 42 | *.PDF diff=astextplain 43 | *.rtf diff=astextplain 44 | *.RTF diff=astextplain 45 | *.markdown text 46 | *.md text 47 | *.adoc text 48 | *.textile text 49 | *.mustache text 50 | *.csv text 51 | *.tab text 52 | *.tsv text 53 | *.txt text 54 | AUTHORS text 55 | CHANGELOG text 56 | CHANGES text 57 | CONTRIBUTING text 58 | COPYING text 59 | copyright text 60 | *COPYRIGHT* text 61 | INSTALL text 62 | license text 63 | LICENSE text 64 | NEWS text 65 | readme text 66 | *README* text 67 | TODO text 68 | 69 | # Graphics 70 | *.png binary 71 | *.jpg binary 72 | *.jpeg binary 73 | *.gif binary 74 | *.tif binary 75 | *.tiff binary 76 | *.ico binary 77 | # SVG treated as an asset (binary) by default. If you want to treat it as text, 78 | # comment-out the following line and uncomment the line after. 79 | *.svg binary 80 | #*.svg text 81 | *.eps binary 82 | 83 | # These files are binary and should be left untouched 84 | # (binary is a macro for -text -diff) 85 | *.class binary 86 | *.jar binary 87 | *.war binary 88 | 89 | ## LINTERS 90 | .csslintrc text 91 | .eslintrc text 92 | .jscsrc text 93 | .jshintrc text 94 | .jshintignore text 95 | .stylelintrc text 96 | 97 | ## CONFIGS 98 | *.conf text 99 | *.config text 100 | .editorconfig text 101 | .gitattributes text 102 | .gitconfig text 103 | .gitignore text 104 | .htaccess text 105 | *.npmignore text 106 | 107 | ## HEROKU 108 | Procfile text 109 | .slugignore text 110 | 111 | ## AUDIO 112 | *.kar binary 113 | *.m4a binary 114 | *.mid binary 115 | *.midi binary 116 | *.mp3 binary 117 | *.ogg binary 118 | *.ra binary 119 | 120 | ## VIDEO 121 | *.3gpp binary 122 | *.3gp binary 123 | *.as binary 124 | *.asf binary 125 | *.asx binary 126 | *.fla binary 127 | *.flv binary 128 | *.m4v binary 129 | *.mng binary 130 | *.mov binary 131 | *.mp4 binary 132 | *.mpeg binary 133 | *.mpg binary 134 | *.swc binary 135 | *.swf binary 136 | *.webm binary 137 | 138 | ## ARCHIVES 139 | *.7z binary 140 | *.gz binary 141 | *.rar binary 142 | *.tar binary 143 | *.zip binary 144 | 145 | ## FONTS 146 | *.ttf binary 147 | *.eot binary 148 | *.otf binary 149 | *.woff binary 150 | *.woff2 binary 151 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/BookKafkaResource.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest; 2 | 3 | import com.skcc.book.config.KafkaProperties; 4 | import org.apache.kafka.clients.consumer.ConsumerRecord; 5 | import org.apache.kafka.clients.consumer.ConsumerRecords; 6 | import org.apache.kafka.clients.consumer.KafkaConsumer; 7 | import org.apache.kafka.clients.producer.KafkaProducer; 8 | import org.apache.kafka.clients.producer.ProducerRecord; 9 | import org.apache.kafka.clients.producer.RecordMetadata; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.web.bind.annotation.*; 13 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 14 | 15 | import java.time.Duration; 16 | import java.time.Instant; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.ExecutorService; 21 | import java.util.concurrent.Executors; 22 | 23 | @RestController 24 | @RequestMapping("/api/book-kafka") 25 | public class BookKafkaResource { 26 | 27 | private final Logger log = LoggerFactory.getLogger(BookKafkaResource.class); 28 | 29 | private final KafkaProperties kafkaProperties; 30 | private KafkaProducer producer; 31 | private ExecutorService sseExecutorService = Executors.newCachedThreadPool(); 32 | 33 | public BookKafkaResource(KafkaProperties kafkaProperties) { 34 | this.kafkaProperties = kafkaProperties; 35 | this.producer = new KafkaProducer<>(kafkaProperties.getProducerProps()); 36 | } 37 | 38 | @PostMapping("/publish/{topic}") 39 | public PublishResult publish(@PathVariable String topic, @RequestParam String message, @RequestParam(required = false) String key) throws ExecutionException, InterruptedException { 40 | log.debug("REST request to send to Kafka topic {} with key {} the message : {}", topic, key, message); 41 | RecordMetadata metadata = producer.send(new ProducerRecord<>(topic, key, message)).get(); 42 | return new PublishResult(metadata.topic(), metadata.partition(), metadata.offset(), Instant.ofEpochMilli(metadata.timestamp())); 43 | } 44 | 45 | @GetMapping("/consume") 46 | public SseEmitter consume(@RequestParam("topic") List topics, @RequestParam Map consumerParams) { 47 | log.debug("REST request to consume records from Kafka topics {}", topics); 48 | Map consumerProps = kafkaProperties.getConsumerProps(); 49 | consumerProps.putAll(consumerParams); 50 | consumerProps.remove("topic"); 51 | 52 | SseEmitter emitter = new SseEmitter(0L); 53 | sseExecutorService.execute(() -> { 54 | KafkaConsumer consumer = new KafkaConsumer<>(consumerProps); 55 | emitter.onCompletion(consumer::close); 56 | consumer.subscribe(topics); 57 | boolean exitLoop = false; 58 | while(!exitLoop) { 59 | try { 60 | ConsumerRecords records = consumer.poll(Duration.ofSeconds(5)); 61 | for (ConsumerRecord record : records) { 62 | emitter.send(record.value()); 63 | } 64 | emitter.send(SseEmitter.event().comment("")); 65 | } catch (Exception ex) { 66 | log.trace("Complete with error {}", ex.getMessage(), ex); 67 | emitter.completeWithError(ex); 68 | exitLoop = true; 69 | } 70 | } 71 | consumer.close(); 72 | emitter.complete(); 73 | }); 74 | return emitter; 75 | } 76 | 77 | private static class PublishResult { 78 | public final String topic; 79 | public final int partition; 80 | public final long offset; 81 | public final Instant timestamp; 82 | 83 | private PublishResult(String topic, int partition, long offset, Instant timestamp) { 84 | this.topic = topic; 85 | this.partition = partition; 86 | this.offset = offset; 87 | this.timestamp = timestamp; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/dto/BookDTO.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.dto; 2 | 3 | import java.time.LocalDate; 4 | import java.io.Serializable; 5 | import java.util.Objects; 6 | import java.util.Set; 7 | 8 | import com.skcc.book.domain.enumeration.Classification; 9 | import com.skcc.book.domain.enumeration.BookStatus; 10 | import com.skcc.book.domain.enumeration.Location; 11 | 12 | import javax.persistence.Convert; 13 | 14 | /** 15 | * A DTO for the {@link com.skcc.book.domain.Book} entity. 16 | */ 17 | 18 | public class BookDTO implements Serializable { 19 | 20 | private Long id; 21 | 22 | private String title; 23 | 24 | private String description; 25 | 26 | private String author; 27 | 28 | private String publisher; 29 | 30 | private Long isbn; 31 | 32 | private LocalDate publicationDate; 33 | 34 | private Classification classification; 35 | 36 | private BookStatus bookStatus; 37 | 38 | private Location location; 39 | 40 | public Long getId() { 41 | return id; 42 | } 43 | 44 | public void setId(Long id) { 45 | this.id = id; 46 | } 47 | 48 | public String getTitle() { 49 | return title; 50 | } 51 | 52 | public void setTitle(String title) { 53 | this.title = title; 54 | } 55 | 56 | public String getDescription() { 57 | return description; 58 | } 59 | 60 | public void setDescription(String description) { 61 | this.description = description; 62 | } 63 | 64 | public String getAuthor() { 65 | return author; 66 | } 67 | 68 | public void setAuthor(String author) { 69 | this.author = author; 70 | } 71 | 72 | public String getPublisher() { 73 | return publisher; 74 | } 75 | 76 | public void setPublisher(String publisher) { 77 | this.publisher = publisher; 78 | } 79 | 80 | public Long getIsbn() { 81 | return isbn; 82 | } 83 | 84 | public void setIsbn(Long isbn) { 85 | this.isbn = isbn; 86 | } 87 | 88 | public LocalDate getPublicationDate() { 89 | return publicationDate; 90 | } 91 | 92 | public void setPublicationDate(LocalDate publicationDate) { 93 | this.publicationDate = publicationDate; 94 | } 95 | 96 | public Classification getClassification() { 97 | return classification; 98 | } 99 | 100 | public void setClassification(Classification classification) { 101 | this.classification = classification; 102 | } 103 | 104 | public BookStatus getBookStatus() { 105 | return bookStatus; 106 | } 107 | 108 | public void setBookStatus(BookStatus bookStatus) { 109 | this.bookStatus = bookStatus; 110 | } 111 | 112 | public Location getLocation() { 113 | return location; 114 | } 115 | 116 | public void setLocation(Location location) { 117 | this.location = location; 118 | } 119 | 120 | @Override 121 | public boolean equals(Object o) { 122 | if (this == o) { 123 | return true; 124 | } 125 | if (o == null || getClass() != o.getClass()) { 126 | return false; 127 | } 128 | 129 | BookDTO bookDTO = (BookDTO) o; 130 | if (bookDTO.getId() == null || getId() == null) { 131 | return false; 132 | } 133 | return Objects.equals(getId(), bookDTO.getId()); 134 | } 135 | 136 | @Override 137 | public int hashCode() { 138 | return Objects.hashCode(getId()); 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | return "BookDTO{" + 144 | "id=" + getId() + 145 | ", title='" + getTitle() + "'" + 146 | ", description='" + getDescription() + "'" + 147 | ", author='" + getAuthor() + "'" + 148 | ", publisher='" + getPublisher() + "'" + 149 | ", isbn=" + getIsbn() + 150 | ", publicationDate='" + getPublicationDate() + "'" + 151 | ", classification='" + getClassification() + "'" + 152 | ", bookStatus='" + getBookStatus() + "'" + 153 | ", location='" + getLocation() + "'" + 154 | "}"; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/resources/config/liquibase/changelog/20200506084946_added_entity_Book.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 66 | 67 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JHipster microservice homepage 6 | 72 | 73 | 74 |

75 |

Welcome, Java Hipster!

76 | 77 |

This application is a microservice, which has been generated using JHipster.

78 | 79 |
    80 |
  • It does not have a front-end. The front-end should be generated on a JHipster gateway.
  • 81 |
  • It is serving REST APIs, under the '/api' URLs.
  • 82 |
  • To manage this microservice, you will probably want to use the JHipster Registry: 83 |
      84 |
    • To run the JHipster Registry locally, you can use Docker:
      docker-compose -f src/main/docker/jhipster-registry.yml up -d
    • 85 |
    • Its default URL is http://localhost:8761/ and its default login/password is admin/admin
    • 86 |
    87 |
  • 88 |
  • Swagger documentation endpoint for those APIs is at /v2/api-docs, but if you want access to the full Swagger UI, you should use a JHipster gateway or a JHipster Registry, which will serve as API developer portals.
  • 89 |
90 | 91 |

92 | If you have any question on JHipster: 93 |

94 | 95 | 102 | 103 |

104 | If you like JHipster, don't forget to give us a star on GitHub! 105 |

106 | 107 |
108 | 109 | 110 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/security/jwt/TokenProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security.jwt; 2 | 3 | import com.skcc.book.security.AuthoritiesConstants; 4 | 5 | import java.security.Key; 6 | import java.util.*; 7 | 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.core.GrantedAuthority; 13 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 14 | import org.springframework.test.util.ReflectionTestUtils; 15 | 16 | import io.github.jhipster.config.JHipsterProperties; 17 | import io.jsonwebtoken.Jwts; 18 | import io.jsonwebtoken.SignatureAlgorithm; 19 | import io.jsonwebtoken.io.Decoders; 20 | import io.jsonwebtoken.security.Keys; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | public class TokenProviderTest { 25 | 26 | private static final long ONE_MINUTE = 60000; 27 | 28 | private Key key; 29 | private TokenProvider tokenProvider; 30 | 31 | @BeforeEach 32 | public void setup() { 33 | tokenProvider = new TokenProvider( new JHipsterProperties()); 34 | key = Keys.hmacShaKeyFor(Decoders.BASE64 35 | .decode("fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8")); 36 | 37 | ReflectionTestUtils.setField(tokenProvider, "key", key); 38 | ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE); 39 | } 40 | 41 | @Test 42 | public void testReturnFalseWhenJWThasInvalidSignature() { 43 | boolean isTokenValid = tokenProvider.validateToken(createTokenWithDifferentSignature()); 44 | 45 | assertThat(isTokenValid).isEqualTo(false); 46 | } 47 | 48 | @Test 49 | public void testReturnFalseWhenJWTisMalformed() { 50 | Authentication authentication = createAuthentication(); 51 | String token = tokenProvider.createToken(authentication, false); 52 | String invalidToken = token.substring(1); 53 | boolean isTokenValid = tokenProvider.validateToken(invalidToken); 54 | 55 | assertThat(isTokenValid).isEqualTo(false); 56 | } 57 | 58 | @Test 59 | public void testReturnFalseWhenJWTisExpired() { 60 | ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", -ONE_MINUTE); 61 | 62 | Authentication authentication = createAuthentication(); 63 | String token = tokenProvider.createToken(authentication, false); 64 | 65 | boolean isTokenValid = tokenProvider.validateToken(token); 66 | 67 | assertThat(isTokenValid).isEqualTo(false); 68 | } 69 | 70 | @Test 71 | public void testReturnFalseWhenJWTisUnsupported() { 72 | String unsupportedToken = createUnsupportedToken(); 73 | 74 | boolean isTokenValid = tokenProvider.validateToken(unsupportedToken); 75 | 76 | assertThat(isTokenValid).isEqualTo(false); 77 | } 78 | 79 | @Test 80 | public void testReturnFalseWhenJWTisInvalid() { 81 | boolean isTokenValid = tokenProvider.validateToken(""); 82 | 83 | assertThat(isTokenValid).isEqualTo(false); 84 | } 85 | 86 | private Authentication createAuthentication() { 87 | Collection authorities = new ArrayList<>(); 88 | authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); 89 | return new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities); 90 | } 91 | 92 | private String createUnsupportedToken() { 93 | return Jwts.builder() 94 | .setPayload("payload") 95 | .signWith(key, SignatureAlgorithm.HS512) 96 | .compact(); 97 | } 98 | 99 | private String createTokenWithDifferentSignature() { 100 | Key otherKey = Keys.hmacShaKeyFor(Decoders.BASE64 101 | .decode("Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8")); 102 | 103 | return Jwts.builder() 104 | .setSubject("anonymous") 105 | .signWith(otherKey, SignatureAlgorithm.HS512) 106 | .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) 107 | .compact(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/aop/logging/LoggingAspect.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.aop.logging; 2 | 3 | import io.github.jhipster.config.JHipsterConstants; 4 | 5 | import org.aspectj.lang.JoinPoint; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.annotation.AfterThrowing; 8 | import org.aspectj.lang.annotation.Around; 9 | import org.aspectj.lang.annotation.Aspect; 10 | import org.aspectj.lang.annotation.Pointcut; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.core.env.Environment; 14 | import org.springframework.core.env.Profiles; 15 | 16 | import java.util.Arrays; 17 | 18 | /** 19 | * Aspect for logging execution of service and repository Spring components. 20 | * 21 | * By default, it only runs with the "dev" profile. 22 | */ 23 | @Aspect 24 | public class LoggingAspect { 25 | 26 | private final Environment env; 27 | 28 | public LoggingAspect(Environment env) { 29 | this.env = env; 30 | } 31 | 32 | /** 33 | * Pointcut that matches all repositories, services and Web REST endpoints. 34 | */ 35 | @Pointcut("within(@org.springframework.stereotype.Repository *)" + 36 | " || within(@org.springframework.stereotype.Service *)" + 37 | " || within(@org.springframework.web.bind.annotation.RestController *)") 38 | public void springBeanPointcut() { 39 | // Method is empty as this is just a Pointcut, the implementations are in the advices. 40 | } 41 | 42 | /** 43 | * Pointcut that matches all Spring beans in the application's main packages. 44 | */ 45 | @Pointcut("within(com.skcc.book.repository..*)"+ 46 | " || within(com.skcc.book.service..*)"+ 47 | " || within(com.skcc.book.web.rest..*)") 48 | public void applicationPackagePointcut() { 49 | // Method is empty as this is just a Pointcut, the implementations are in the advices. 50 | } 51 | 52 | /** 53 | * Retrieves the {@link Logger} associated to the given {@link JoinPoint}. 54 | * 55 | * @param joinPoint join point we want the logger for. 56 | * @return {@link Logger} associated to the given {@link JoinPoint}. 57 | */ 58 | private Logger logger(JoinPoint joinPoint) { 59 | return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringTypeName()); 60 | } 61 | 62 | /** 63 | * Advice that logs methods throwing exceptions. 64 | * 65 | * @param joinPoint join point for advice. 66 | * @param e exception. 67 | */ 68 | @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") 69 | public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { 70 | if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { 71 | logger(joinPoint) 72 | .error( 73 | "Exception in {}() with cause = \'{}\' and exception = \'{}\'", 74 | joinPoint.getSignature().getName(), 75 | e.getCause() != null ? e.getCause() : "NULL", 76 | e.getMessage(), 77 | e 78 | ); 79 | } else { 80 | logger(joinPoint) 81 | .error( 82 | "Exception in {}() with cause = {}", 83 | joinPoint.getSignature().getName(), 84 | e.getCause() != null ? e.getCause() : "NULL" 85 | ); 86 | } 87 | } 88 | 89 | /** 90 | * Advice that logs when a method is entered and exited. 91 | * 92 | * @param joinPoint join point for advice. 93 | * @return result. 94 | * @throws Throwable throws {@link IllegalArgumentException}. 95 | */ 96 | @Around("applicationPackagePointcut() && springBeanPointcut()") 97 | public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { 98 | Logger log = logger(joinPoint); 99 | if (log.isDebugEnabled()) { 100 | log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs())); 101 | } 102 | try { 103 | Object result = joinPoint.proceed(); 104 | if (log.isDebugEnabled()) { 105 | log.debug("Exit: {}() with result = {}", joinPoint.getSignature().getName(), result); 106 | } 107 | return result; 108 | } catch (IllegalArgumentException e) { 109 | log.error("Illegal argument: {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName()); 110 | throw e; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/security/jwt/TokenProvider.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.security.jwt; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.security.Key; 5 | import java.util.*; 6 | import java.util.stream.Collectors; 7 | import javax.annotation.PostConstruct; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.core.GrantedAuthority; 14 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 15 | import org.springframework.security.core.userdetails.User; 16 | import org.springframework.stereotype.Component; 17 | import org.springframework.util.StringUtils; 18 | 19 | import io.github.jhipster.config.JHipsterProperties; 20 | import io.jsonwebtoken.*; 21 | import io.jsonwebtoken.io.Decoders; 22 | import io.jsonwebtoken.security.Keys; 23 | 24 | @Component 25 | public class TokenProvider { 26 | 27 | private final Logger log = LoggerFactory.getLogger(TokenProvider.class); 28 | 29 | private static final String AUTHORITIES_KEY = "auth"; 30 | 31 | private Key key; 32 | 33 | private long tokenValidityInMilliseconds; 34 | 35 | private long tokenValidityInMillisecondsForRememberMe; 36 | 37 | private final JHipsterProperties jHipsterProperties; 38 | 39 | public TokenProvider(JHipsterProperties jHipsterProperties) { 40 | this.jHipsterProperties = jHipsterProperties; 41 | } 42 | 43 | @PostConstruct 44 | public void init() { 45 | byte[] keyBytes; 46 | String secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret(); 47 | if (!StringUtils.isEmpty(secret)) { 48 | log.warn("Warning: the JWT key used is not Base64-encoded. " + 49 | "We recommend using the `jhipster.security.authentication.jwt.base64-secret` key for optimum security."); 50 | keyBytes = secret.getBytes(StandardCharsets.UTF_8); 51 | } else { 52 | log.debug("Using a Base64-encoded JWT secret key"); 53 | keyBytes = Decoders.BASE64.decode(jHipsterProperties.getSecurity().getAuthentication().getJwt().getBase64Secret()); 54 | } 55 | this.key = Keys.hmacShaKeyFor(keyBytes); 56 | this.tokenValidityInMilliseconds = 57 | 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); 58 | this.tokenValidityInMillisecondsForRememberMe = 59 | 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt() 60 | .getTokenValidityInSecondsForRememberMe(); 61 | } 62 | 63 | public String createToken(Authentication authentication, boolean rememberMe) { 64 | String authorities = authentication.getAuthorities().stream() 65 | .map(GrantedAuthority::getAuthority) 66 | .collect(Collectors.joining(",")); 67 | 68 | long now = (new Date()).getTime(); 69 | Date validity; 70 | if (rememberMe) { 71 | validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe); 72 | } else { 73 | validity = new Date(now + this.tokenValidityInMilliseconds); 74 | } 75 | 76 | return Jwts.builder() 77 | .setSubject(authentication.getName()) 78 | .claim(AUTHORITIES_KEY, authorities) 79 | .signWith(key, SignatureAlgorithm.HS512) 80 | .setExpiration(validity) 81 | .compact(); 82 | } 83 | 84 | public Authentication getAuthentication(String token) { 85 | Claims claims = Jwts.parser() 86 | .setSigningKey(key) 87 | .parseClaimsJws(token) 88 | .getBody(); 89 | 90 | Collection authorities = 91 | Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) 92 | .map(SimpleGrantedAuthority::new) 93 | .collect(Collectors.toList()); 94 | 95 | User principal = new User(claims.getSubject(), "", authorities); 96 | 97 | return new UsernamePasswordAuthenticationToken(principal, token, authorities); 98 | } 99 | 100 | public boolean validateToken(String authToken) { 101 | try { 102 | Jwts.parser().setSigningKey(key).parseClaimsJws(authToken); 103 | return true; 104 | } catch (JwtException | IllegalArgumentException e) { 105 | log.info("Invalid JWT token."); 106 | log.trace("Invalid JWT token trace.", e); 107 | } 108 | return false; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/resources/config/application.yml: -------------------------------------------------------------------------------- 1 | # =================================================================== 2 | # Spring Boot configuration. 3 | # 4 | # This configuration is used for unit/integration tests. 5 | # 6 | # More information on profiles: https://www.jhipster.tech/profiles/ 7 | # More information on configuration properties: https://www.jhipster.tech/common-application-properties/ 8 | # =================================================================== 9 | 10 | # =================================================================== 11 | # Standard Spring Boot properties. 12 | # Full reference is available at: 13 | # http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 14 | # =================================================================== 15 | 16 | eureka: 17 | client: 18 | enabled: false 19 | instance: 20 | appname: book 21 | instanceId: book:${spring.application.instance-id:${random.value}} 22 | 23 | spring: 24 | application: 25 | name: book 26 | datasource: 27 | type: com.zaxxer.hikari.HikariDataSource 28 | url: jdbc:h2:mem:book;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 29 | name: 30 | username: 31 | password: 32 | hikari: 33 | auto-commit: false 34 | jackson: 35 | serialization: 36 | write-durations-as-timestamps: false 37 | jpa: 38 | database-platform: io.github.jhipster.domain.util.FixedH2Dialect 39 | open-in-view: false 40 | show-sql: false 41 | hibernate: 42 | ddl-auto: none 43 | naming: 44 | physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy 45 | implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy 46 | properties: 47 | hibernate.id.new_generator_mappings: true 48 | hibernate.connection.provider_disables_autocommit: true 49 | hibernate.cache.use_second_level_cache: false 50 | hibernate.cache.use_query_cache: false 51 | hibernate.generate_statistics: false 52 | hibernate.hbm2ddl.auto: validate 53 | hibernate.jdbc.time_zone: UTC 54 | liquibase: 55 | contexts: test 56 | mail: 57 | host: localhost 58 | main: 59 | allow-bean-definition-overriding: true 60 | messages: 61 | basename: i18n/messages 62 | mvc: 63 | favicon: 64 | enabled: false 65 | task: 66 | execution: 67 | thread-name-prefix: book-task- 68 | pool: 69 | core-size: 1 70 | max-size: 50 71 | queue-capacity: 10000 72 | scheduling: 73 | thread-name-prefix: book-scheduling- 74 | pool: 75 | size: 1 76 | thymeleaf: 77 | mode: HTML 78 | 79 | server: 80 | port: 10344 81 | address: localhost 82 | 83 | # =================================================================== 84 | # JHipster specific properties 85 | # 86 | # Full reference is available at: https://www.jhipster.tech/common-application-properties/ 87 | # =================================================================== 88 | 89 | jhipster: 90 | clientApp: 91 | name: 'bookApp' 92 | logging: 93 | # To test json console appender 94 | use-json-format: true # By default, logs are in Json format 95 | logstash: 96 | enabled: false 97 | host: localhost 98 | port: 5000 99 | queue-size: 512 100 | security: 101 | authentication: 102 | jwt: 103 | # This token must be encoded using Base64 (you can type `echo 'secret-key'|base64` on your command line) 104 | base64-secret: ZDI0MmE4ZDIzMzQ5OTRmZThlZjU5NzZlMDY3NDEzOTYxMDhlNmZjOGYwNzE3ODBkMGM4NGJhMTY5NDBkYWEzMjg2MmYxODljNmI4NGJlY2JlZmRmNGUxN2E3NWRmNzY0ZjZiYmExZjQxMzgyMTU2NjZiMzY0OTIzYmE1ZTUxN2Q= 105 | # Token is valid 24 hours 106 | token-validity-in-seconds: 86400 107 | metrics: 108 | logs: # Reports metrics in the logs 109 | enabled: true 110 | report-frequency: 60 # in seconds 111 | kafka: 112 | bootstrap-servers: localhost:9092 113 | consumer: 114 | key.deserializer: org.apache.kafka.common.serialization.StringDeserializer 115 | value.deserializer: org.apache.kafka.common.serialization.StringDeserializer 116 | group.id: book 117 | auto.offset.reset: earliest 118 | producer: 119 | key.serializer: org.apache.kafka.common.serialization.StringSerializer 120 | value.serializer: org.apache.kafka.common.serialization.StringSerializer 121 | # =================================================================== 122 | # Application specific properties 123 | # Add your own application properties here, see the ApplicationProperties class 124 | # to have type-safe configuration, like in the JHipsterProperties above 125 | # 126 | # More documentation is available at: 127 | # https://www.jhipster.tech/common-application-properties/ 128 | # =================================================================== 129 | 130 | # application: 131 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/BookApp.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book; 2 | 3 | import com.skcc.book.config.ApplicationProperties; 4 | 5 | import io.github.jhipster.config.DefaultProfileUtil; 6 | import io.github.jhipster.config.JHipsterConstants; 7 | 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.SpringApplication; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; 14 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 15 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 16 | import org.springframework.core.env.Environment; 17 | 18 | import javax.annotation.PostConstruct; 19 | import java.net.InetAddress; 20 | import java.net.UnknownHostException; 21 | import java.util.Arrays; 22 | import java.util.Collection; 23 | 24 | @SpringBootApplication 25 | @EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class}) 26 | @EnableDiscoveryClient 27 | public class BookApp { 28 | 29 | private static final Logger log = LoggerFactory.getLogger(BookApp.class); 30 | 31 | private final Environment env; 32 | 33 | public BookApp(Environment env) { 34 | this.env = env; 35 | } 36 | 37 | /** 38 | * Initializes book. 39 | *

40 | * Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile 41 | *

42 | * You can find more information on how profiles work with JHipster on https://www.jhipster.tech/profiles/. 43 | */ 44 | @PostConstruct 45 | public void initApplication() { 46 | Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); 47 | if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { 48 | log.error("You have misconfigured your application! It should not run " + 49 | "with both the 'dev' and 'prod' profiles at the same time."); 50 | } 51 | if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) { 52 | log.error("You have misconfigured your application! It should not " + 53 | "run with both the 'dev' and 'cloud' profiles at the same time."); 54 | } 55 | } 56 | 57 | /** 58 | * Main method, used to run the application. 59 | * 60 | * @param args the command line arguments. 61 | */ 62 | public static void main(String[] args) { 63 | SpringApplication app = new SpringApplication(BookApp.class); 64 | DefaultProfileUtil.addDefaultProfile(app); 65 | Environment env = app.run(args).getEnvironment(); 66 | logApplicationStartup(env); 67 | } 68 | 69 | private static void logApplicationStartup(Environment env) { 70 | String protocol = "http"; 71 | if (env.getProperty("server.ssl.key-store") != null) { 72 | protocol = "https"; 73 | } 74 | String serverPort = env.getProperty("server.port"); 75 | String contextPath = env.getProperty("server.servlet.context-path"); 76 | if (StringUtils.isBlank(contextPath)) { 77 | contextPath = "/"; 78 | } 79 | String hostAddress = "localhost"; 80 | try { 81 | hostAddress = InetAddress.getLocalHost().getHostAddress(); 82 | } catch (UnknownHostException e) { 83 | log.warn("The host name could not be determined, using `localhost` as fallback"); 84 | } 85 | log.info("\n----------------------------------------------------------\n\t" + 86 | "Application '{}' is running! Access URLs:\n\t" + 87 | "Local: \t\t{}://localhost:{}{}\n\t" + 88 | "External: \t{}://{}:{}{}\n\t" + 89 | "Profile(s): \t{}\n----------------------------------------------------------", 90 | env.getProperty("spring.application.name"), 91 | protocol, 92 | serverPort, 93 | contextPath, 94 | protocol, 95 | hostAddress, 96 | serverPort, 97 | contextPath, 98 | env.getActiveProfiles()); 99 | 100 | String configServerStatus = env.getProperty("configserver.status"); 101 | if (configServerStatus == null) { 102 | configServerStatus = "Not found or not setup for this application"; 103 | } 104 | log.info("\n----------------------------------------------------------\n\t" + 105 | "Config Server: \t{}\n----------------------------------------------------------", configServerStatus); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # book 2 | 3 | This application was generated using JHipster 6.8.0, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v6.8.0](https://www.jhipster.tech/documentation-archive/v6.8.0). 4 | 5 | This is a "microservice" application intended to be part of a microservice architecture, please refer to the [Doing microservices with JHipster][] page of the documentation for more information. 6 | 7 | This application is configured for Service Discovery and Configuration with the JHipster-Registry. On launch, it will refuse to start if it is not able to connect to the JHipster-Registry at [http://localhost:8761](http://localhost:8761). For more information, read our documentation on [Service Discovery and Configuration with the JHipster-Registry][]. 8 | 9 | ## Development 10 | 11 | To start your application in the dev profile, run: 12 | 13 | ./mvnw 14 | 15 | For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][]. 16 | 17 | ## Building for production 18 | 19 | ### Packaging as jar 20 | 21 | To build the final jar and optimize the book application for production, run: 22 | 23 | ./mvnw -Pprod clean verify 24 | 25 | To ensure everything worked, run: 26 | 27 | java -jar target/*.jar 28 | 29 | Refer to [Using JHipster in production][] for more details. 30 | 31 | ### Packaging as war 32 | 33 | To package your application as a war in order to deploy it to an application server, run: 34 | 35 | ./mvnw -Pprod,war clean verify 36 | 37 | ## Testing 38 | 39 | To launch your application's tests, run: 40 | 41 | ./mvnw verify 42 | 43 | For more information, refer to the [Running tests page][]. 44 | 45 | ### Code quality 46 | 47 | Sonar is used to analyse code quality. You can start a local Sonar server (accessible on http://localhost:9001) with: 48 | 49 | ``` 50 | docker-compose -f src/main/docker/sonar.yml up -d 51 | ``` 52 | 53 | You can run a Sonar analysis with using the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) or by using the maven plugin. 54 | 55 | Then, run a Sonar analysis: 56 | 57 | ``` 58 | ./mvnw -Pprod clean verify sonar:sonar 59 | ``` 60 | 61 | If you need to re-run the Sonar phase, please be sure to specify at least the `initialize` phase since Sonar properties are loaded from the sonar-project.properties file. 62 | 63 | ``` 64 | ./mvnw initialize sonar:sonar 65 | ``` 66 | 67 | or 68 | 69 | For more information, refer to the [Code quality page][]. 70 | 71 | ## Using Docker to simplify development (optional) 72 | 73 | You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services. 74 | 75 | For example, to start a mariadb database in a docker container, run: 76 | 77 | docker-compose -f src/main/docker/mariadb.yml up -d 78 | 79 | To stop it and remove the container, run: 80 | 81 | docker-compose -f src/main/docker/mariadb.yml down 82 | 83 | You can also fully dockerize your application and all the services that it depends on. 84 | To achieve this, first build a docker image of your app by running: 85 | 86 | ./mvnw -Pprod verify jib:dockerBuild 87 | 88 | Then run: 89 | 90 | docker-compose -f src/main/docker/app.yml up -d 91 | 92 | For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`jhipster docker-compose`), which is able to generate docker configurations for one or several JHipster applications. 93 | 94 | ## Continuous Integration (optional) 95 | 96 | To configure CI for your project, run the ci-cd sub-generator (`jhipster ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information. 97 | 98 | [jhipster homepage and latest documentation]: https://www.jhipster.tech 99 | [jhipster 6.8.0 archive]: https://www.jhipster.tech/documentation-archive/v6.8.0 100 | [doing microservices with jhipster]: https://www.jhipster.tech/documentation-archive/v6.8.0/microservices-architecture/ 101 | [using jhipster in development]: https://www.jhipster.tech/documentation-archive/v6.8.0/development/ 102 | [service discovery and configuration with the jhipster-registry]: https://www.jhipster.tech/documentation-archive/v6.8.0/microservices-architecture/#jhipster-registry 103 | [using docker and docker-compose]: https://www.jhipster.tech/documentation-archive/v6.8.0/docker-compose 104 | [using jhipster in production]: https://www.jhipster.tech/documentation-archive/v6.8.0/production/ 105 | [running tests page]: https://www.jhipster.tech/documentation-archive/v6.8.0/running-tests/ 106 | [code quality page]: https://www.jhipster.tech/documentation-archive/v6.8.0/code-quality/ 107 | [setting up continuous integration]: https://www.jhipster.tech/documentation-archive/v6.8.0/setting-up-ci/ 108 | -------------------------------------------------------------------------------- /src/main/java/com/skcc/book/web/rest/errors/ExceptionTranslator.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest.errors; 2 | 3 | import io.github.jhipster.web.util.HeaderUtil; 4 | 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.dao.ConcurrencyFailureException; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.validation.BindingResult; 9 | import org.springframework.web.bind.MethodArgumentNotValidException; 10 | import org.springframework.web.bind.annotation.ControllerAdvice; 11 | import org.springframework.web.bind.annotation.ExceptionHandler; 12 | import org.springframework.web.context.request.NativeWebRequest; 13 | import org.zalando.problem.DefaultProblem; 14 | import org.zalando.problem.Problem; 15 | import org.zalando.problem.ProblemBuilder; 16 | import org.zalando.problem.Status; 17 | import org.zalando.problem.spring.web.advice.ProblemHandling; 18 | import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait; 19 | import org.zalando.problem.violations.ConstraintViolationProblem; 20 | 21 | import javax.annotation.Nonnull; 22 | import javax.annotation.Nullable; 23 | import javax.servlet.http.HttpServletRequest; 24 | import java.util.List; 25 | import java.util.stream.Collectors; 26 | 27 | /** 28 | * Controller advice to translate the server side exceptions to client-friendly json structures. 29 | * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807). 30 | */ 31 | @ControllerAdvice 32 | public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { 33 | 34 | private static final String FIELD_ERRORS_KEY = "fieldErrors"; 35 | private static final String MESSAGE_KEY = "message"; 36 | private static final String PATH_KEY = "path"; 37 | private static final String VIOLATIONS_KEY = "violations"; 38 | 39 | @Value("${jhipster.clientApp.name}") 40 | private String applicationName; 41 | 42 | /** 43 | * Post-process the Problem payload to add the message key for the front-end if needed. 44 | */ 45 | @Override 46 | public ResponseEntity process(@Nullable ResponseEntity entity, NativeWebRequest request) { 47 | if (entity == null) { 48 | return entity; 49 | } 50 | Problem problem = entity.getBody(); 51 | if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) { 52 | return entity; 53 | } 54 | ProblemBuilder builder = Problem.builder() 55 | .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType()) 56 | .withStatus(problem.getStatus()) 57 | .withTitle(problem.getTitle()) 58 | .with(PATH_KEY, request.getNativeRequest(HttpServletRequest.class).getRequestURI()); 59 | 60 | if (problem instanceof ConstraintViolationProblem) { 61 | builder 62 | .with(VIOLATIONS_KEY, ((ConstraintViolationProblem) problem).getViolations()) 63 | .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION); 64 | } else { 65 | builder 66 | .withCause(((DefaultProblem) problem).getCause()) 67 | .withDetail(problem.getDetail()) 68 | .withInstance(problem.getInstance()); 69 | problem.getParameters().forEach(builder::with); 70 | if (!problem.getParameters().containsKey(MESSAGE_KEY) && problem.getStatus() != null) { 71 | builder.with(MESSAGE_KEY, "error.http." + problem.getStatus().getStatusCode()); 72 | } 73 | } 74 | return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode()); 75 | } 76 | 77 | @Override 78 | public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) { 79 | BindingResult result = ex.getBindingResult(); 80 | List fieldErrors = result.getFieldErrors().stream() 81 | .map(f -> new FieldErrorVM(f.getObjectName().replaceFirst("DTO$", ""), f.getField(), f.getCode())) 82 | .collect(Collectors.toList()); 83 | 84 | Problem problem = Problem.builder() 85 | .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE) 86 | .withTitle("Method argument not valid") 87 | .withStatus(defaultConstraintViolationStatus()) 88 | .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION) 89 | .with(FIELD_ERRORS_KEY, fieldErrors) 90 | .build(); 91 | return create(ex, problem, request); 92 | } 93 | 94 | @ExceptionHandler 95 | public ResponseEntity handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) { 96 | return create(ex, request, HeaderUtil.createFailureAlert(applicationName, true, ex.getEntityName(), ex.getErrorKey(), ex.getMessage())); 97 | } 98 | 99 | @ExceptionHandler 100 | public ResponseEntity handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) { 101 | Problem problem = Problem.builder() 102 | .withStatus(Status.CONFLICT) 103 | .with(MESSAGE_KEY, ErrorConstants.ERR_CONCURRENCY_FAILURE) 104 | .build(); 105 | return create(ex, problem, request); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/skcc/book/web/rest/BookKafkaResourceIT.java: -------------------------------------------------------------------------------- 1 | package com.skcc.book.web.rest; 2 | 3 | import com.skcc.book.config.KafkaProperties; 4 | import org.apache.kafka.clients.consumer.ConsumerRecord; 5 | import org.apache.kafka.clients.consumer.ConsumerRecords; 6 | import org.apache.kafka.clients.consumer.KafkaConsumer; 7 | import org.apache.kafka.clients.producer.KafkaProducer; 8 | import org.apache.kafka.clients.producer.ProducerRecord; 9 | import org.junit.jupiter.api.BeforeAll; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.test.web.servlet.MockMvc; 14 | import org.springframework.test.web.servlet.MvcResult; 15 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 16 | import org.testcontainers.containers.KafkaContainer; 17 | 18 | import java.time.Duration; 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | import static org.junit.jupiter.api.Assertions.fail; 25 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 26 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 27 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 28 | 29 | class BookKafkaResourceIT { 30 | 31 | private static boolean started = false; 32 | private static KafkaContainer kafkaContainer; 33 | 34 | private MockMvc restMockMvc; 35 | 36 | @BeforeAll 37 | static void startServer() { 38 | if (!started) { 39 | startTestcontainer(); 40 | started = true; 41 | } 42 | } 43 | 44 | private static void startTestcontainer() { 45 | kafkaContainer = new KafkaContainer("5.4.0"); 46 | kafkaContainer.start(); 47 | } 48 | 49 | @BeforeEach 50 | void setup() { 51 | KafkaProperties kafkaProperties = new KafkaProperties(); 52 | Map producerProps = getProducerProps(); 53 | kafkaProperties.setProducer(new HashMap<>(producerProps)); 54 | 55 | Map consumerProps = getConsumerProps("default-group"); 56 | consumerProps.put("client.id", "default-client"); 57 | kafkaProperties.setConsumer(consumerProps); 58 | 59 | BookKafkaResource kafkaResource = new BookKafkaResource(kafkaProperties); 60 | 61 | restMockMvc = MockMvcBuilders.standaloneSetup(kafkaResource).build(); 62 | } 63 | 64 | @Test 65 | void producesMessages() throws Exception { 66 | restMockMvc.perform(post("/api/book-kafka/publish/topic-produce?message=value-produce")) 67 | .andExpect(status().isOk()) 68 | .andExpect(content().contentType(MediaType.APPLICATION_JSON)); 69 | 70 | Map consumerProps = new HashMap<>(getConsumerProps("group-produce")); 71 | KafkaConsumer consumer = new KafkaConsumer<>(consumerProps); 72 | consumer.subscribe(Collections.singletonList("topic-produce")); 73 | ConsumerRecords records = consumer.poll(Duration.ofSeconds(1)); 74 | 75 | assertThat(records.count()).isEqualTo(1); 76 | ConsumerRecord record = records.iterator().next(); 77 | assertThat(record.value()).isEqualTo("value-produce"); 78 | } 79 | 80 | @Test 81 | void consumesMessages() throws Exception { 82 | Map producerProps = new HashMap<>(getProducerProps()); 83 | KafkaProducer producer = new KafkaProducer<>(producerProps); 84 | 85 | producer.send(new ProducerRecord<>("topic-consume", "value-consume")); 86 | 87 | MvcResult mvcResult = restMockMvc.perform(get("/api/book-kafka/consume?topic=topic-consume")) 88 | .andExpect(status().isOk()) 89 | .andExpect(request().asyncStarted()) 90 | .andReturn(); 91 | 92 | for (int i = 0; i < 100; i++) { 93 | Thread.sleep(100); 94 | String content = mvcResult.getResponse().getContentAsString(); 95 | if (content.contains("data:value-consume")) { 96 | return; 97 | } 98 | } 99 | fail("Expected content data:value-consume not received"); 100 | } 101 | 102 | private Map getProducerProps() { 103 | Map producerProps = new HashMap<>(); 104 | producerProps.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); 105 | producerProps.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); 106 | producerProps.put("bootstrap.servers", kafkaContainer.getBootstrapServers()); 107 | return producerProps; 108 | } 109 | 110 | private Map getConsumerProps(String group) { 111 | Map consumerProps = new HashMap<>(); 112 | consumerProps.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); 113 | consumerProps.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); 114 | consumerProps.put("bootstrap.servers", kafkaContainer.getBootstrapServers()); 115 | consumerProps.put("auto.offset.reset", "earliest"); 116 | consumerProps.put("group.id", group); 117 | return consumerProps; 118 | } 119 | } 120 | 121 | --------------------------------------------------------------------------------