diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c27df25
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.1.RELEASE
+
+
+ org.takiguchi
+ starter
+ 0.0.1-SNAPSHOT
+ starter
+ Demo project for Spring Boot
+
+
+ 11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/org/takiguchi/starter/StarterApplication.java b/src/main/java/org/takiguchi/starter/StarterApplication.java
new file mode 100644
index 0000000..ec3f0f9
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/StarterApplication.java
@@ -0,0 +1,11 @@
+package org.takiguchi.starter;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class StarterApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(StarterApplication.class, args);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java b/src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java
new file mode 100644
index 0000000..53aaa87
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/config/security/JwtRequestFilter.java
@@ -0,0 +1,70 @@
+package org.takiguchi.starter.config.security;
+
+import io.jsonwebtoken.Claims;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+import static org.takiguchi.tamotsu.client.constant.Constant.AUTHORITIES_KEY;
+
+public class JwtRequestFilter extends OncePerRequestFilter {
+ private static final String TOKEN_PREFIX = "Bearer ";
+
+ private final TokenProvider tokenProvider;
+
+ public JwtRequestFilter(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
+ String token = getTokenFromHeaders(request);
+
+ // Once we get the token validate it.
+ if (SecurityContextHolder.getContext().getAuthentication() == null) {
+ String username = null;
+ try {
+ username = tokenProvider.getUsernameFromToken(token);
+ } catch (Exception e) {
+ // Do nothing
+ }
+
+ if (username != null && !tokenProvider.isTokenExpired(token)) {
+ Claims claims = tokenProvider.getAllClaimsFromToken(token);
+ List roles = claims.get(AUTHORITIES_KEY, List.class);
+
+ List authorities = roles.stream()
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+
+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, username, authorities);
+ SecurityContextHolder.getContext().setAuthentication(auth);
+ }
+ }
+
+ chain.doFilter(request, response);
+ }
+
+ private String getTokenFromHeaders(HttpServletRequest request) {
+ String token = null;
+
+ String authHeader = request.getHeader(AUTHORIZATION);
+ if (authHeader != null && authHeader.startsWith(TOKEN_PREFIX)) {
+ token = authHeader.replace(TOKEN_PREFIX, "");
+ } else {
+ logger.debug("Couldn't find bearer string, will ignore the header.");
+ }
+
+ return token;
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java b/src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java
new file mode 100644
index 0000000..06a4909
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/config/security/SecurityConfiguration.java
@@ -0,0 +1,59 @@
+package org.takiguchi.starter.config.security;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import javax.servlet.http.HttpServletResponse;
+
+import static org.springframework.http.HttpMethod.GET;
+import static org.springframework.http.HttpMethod.OPTIONS;
+import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+
+@EnableWebSecurity
+@Configuration
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.cors().disable()
+ .exceptionHandling()
+ .authenticationEntryPoint((request, response, authResponse) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
+ .accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN))
+ .and()
+ .addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class)
+ .sessionManagement().sessionCreationPolicy(STATELESS)
+ .and()
+ .requiresChannel()
+ .anyRequest()
+ .requiresSecure()
+ .and()
+ .csrf().disable()
+ .authorizeRequests()
+ .antMatchers(
+ "/api/auth/login",
+ "/api/health/check"
+ ).permitAll()
+ .antMatchers(OPTIONS).permitAll()
+ .anyRequest().authenticated();
+ }
+
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+ @Bean
+ public TokenProvider tokenProvider(@Value("${app.security.signing-key}") String signingKey,
+ @Value("${app.security.access-token-validity-seconds}") int accessTokenValiditySeconds) {
+ return new TokenProvider(signingKey, accessTokenValiditySeconds);
+ }
+
+ @Bean
+ public JwtRequestFilter jwtRequestFilter() {
+ return new JwtRequestFilter(tokenProvider());
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/config/security/TokenProvider.java b/src/main/java/org/takiguchi/starter/config/security/TokenProvider.java
new file mode 100644
index 0000000..2fbca9a
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/config/security/TokenProvider.java
@@ -0,0 +1,70 @@
+package org.takiguchi.starter.config.security;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.springframework.beans.factory.annotation.Value;
+import org.takiguchi.starter.model.dao.User;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Function;
+
+public class TokenProvider {
+
+ private static final String AUTHORITIES_KEY = "scopes";
+ private final String signingKey;
+ private final int accessTokenValiditySeconds;
+
+ public TokenProvider(String signingKey, int accessTokenValiditySeconds) {
+ this.signingKey = signingKey;
+ this.accessTokenValiditySeconds = accessTokenValiditySeconds;
+ }
+
+
+ public String getUserEmailFromToken(String token) {
+ return getClaimFromToken(token, Claims::getSubject);
+ }
+
+ public Date getExpirationDateFromToken(String token) {
+ return getClaimFromToken(token, Claims::getExpiration);
+ }
+
+ public T getClaimFromToken(String token, Function claimsResolver) {
+ Claims claims = getAllClaimsFromToken(token);
+ return claimsResolver.apply(claims);
+ }
+
+ public Boolean isTokenExpired(String token) {
+ final Date expiration = getExpirationDateFromToken(token);
+ return expiration.before(new Date());
+ }
+
+ public Claims getAllClaimsFromToken(String token) {
+ return Jwts.parser()
+ .setSigningKey(signingKey)
+ .parseClaimsJws(token)
+ .getBody();
+ }
+
+ public String generateToken(User user) {
+ return Jwts.builder()
+ .setSubject(user.getEmail())
+ .claim(AUTHORITIES_KEY, getAuthorities(user))
+ .signWith(SignatureAlgorithm.HS256, signingKey)
+ .setIssuedAt(new Date(System.currentTimeMillis()))
+ .setExpiration(new Date(System.currentTimeMillis() + accessTokenValiditySeconds * 1000))
+ .compact();
+ }
+
+ private List getAuthorities(User user) {
+ List authorities = Collections.emptyList();
+// if (!CollectionUtils.isEmpty(user.getRoles())) {
+// authorities = user.getRoles().stream()
+// .map(Role::getName)
+// .collect(Collectors.toList());
+// }
+ return authorities;
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/controller/AuthenticationController.java b/src/main/java/org/takiguchi/starter/controller/AuthenticationController.java
new file mode 100644
index 0000000..828a8f5
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/controller/AuthenticationController.java
@@ -0,0 +1,47 @@
+package org.takiguchi.starter.controller;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.takiguchi.starter.config.security.TokenProvider;
+import org.takiguchi.starter.exception.BadRequestException;
+import org.takiguchi.starter.model.dao.User;
+import org.takiguchi.starter.model.dto.LoginRequest;
+import org.takiguchi.starter.model.dto.LoginResponse;
+import org.takiguchi.starter.repository.UserRepository;
+
+@RestController
+@RequestMapping("/api/auth")
+public class AuthenticationController {
+
+ private final UserRepository userRepository;
+ private final PasswordEncoder passwordEncoder;
+ private final TokenProvider tokenProvider;
+
+ public AuthenticationController(UserRepository userRepository, PasswordEncoder passwordEncoder, TokenProvider tokenProvider) {
+ this.userRepository = userRepository;
+ this.passwordEncoder = passwordEncoder;
+ this.tokenProvider = tokenProvider;
+ }
+
+ @PostMapping("/login")
+ public LoginResponse login(@RequestBody LoginRequest request) {
+ return userRepository.findByEmail(request.getEmail())
+ .map(user -> checkCredentials(request, user))
+ .map(LoginResponse::new)
+ .orElseThrow(() -> new BadRequestException("MSG_INVALID_CREDENTIALS"));
+ }
+
+ /**
+ * If passwords match, this function generate a token. Otherwise, an {@link BadRequestException} is returned.
+ */
+ private String checkCredentials(LoginRequest loginRequest, User user) {
+ if (passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
+ return tokenProvider.generateToken(user);
+ } else {
+ throw new BadRequestException("Invalid credentials");
+ }
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/BadRequestException.java b/src/main/java/org/takiguchi/starter/exception/BadRequestException.java
new file mode 100644
index 0000000..ed25ed0
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/BadRequestException.java
@@ -0,0 +1,15 @@
+package org.takiguchi.starter.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.BAD_REQUEST)
+public class BadRequestException extends BusinessException {
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/BusinessException.java b/src/main/java/org/takiguchi/starter/exception/BusinessException.java
new file mode 100644
index 0000000..4992a39
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/BusinessException.java
@@ -0,0 +1,26 @@
+package org.takiguchi.starter.exception;
+
+/**
+ * Business exception.
+ */
+public class BusinessException extends RuntimeException {
+
+ public BusinessException() {}
+
+ /**
+ * Constructs an exception with a message.
+ * @param message The description of the error met.
+ */
+ public BusinessException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with a message and a code.
+ * @param message The description of the error met.
+ * @param cause The cause of the exception.
+ */
+ public BusinessException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/ForbiddenException.java b/src/main/java/org/takiguchi/starter/exception/ForbiddenException.java
new file mode 100644
index 0000000..3a425e9
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/ForbiddenException.java
@@ -0,0 +1,23 @@
+package org.takiguchi.starter.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception thrown when user attempt to access a resource that he has not rights.
+ */
+@ResponseStatus(value = HttpStatus.FORBIDDEN)
+public class ForbiddenException extends BusinessException {
+
+ public ForbiddenException() {
+ super();
+ }
+
+ public ForbiddenException(String message) {
+ super(message);
+ }
+
+ public ForbiddenException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java b/src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java
new file mode 100644
index 0000000..cc60509
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/InternalServerErrorException.java
@@ -0,0 +1,15 @@
+package org.takiguchi.starter.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+public class InternalServerErrorException extends TechnicalException {
+ public InternalServerErrorException(String message) {
+ super(message);
+ }
+
+ public InternalServerErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/NoContentException.java b/src/main/java/org/takiguchi/starter/exception/NoContentException.java
new file mode 100644
index 0000000..0d31848
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/NoContentException.java
@@ -0,0 +1,17 @@
+package org.takiguchi.starter.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.NO_CONTENT)
+public class NoContentException extends BusinessException {
+ public NoContentException() {}
+
+ public NoContentException(String message) {
+ super(message);
+ }
+
+ public NoContentException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/NotFoundException.java b/src/main/java/org/takiguchi/starter/exception/NotFoundException.java
new file mode 100644
index 0000000..b035b86
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/NotFoundException.java
@@ -0,0 +1,17 @@
+package org.takiguchi.starter.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.NOT_FOUND)
+public class NotFoundException extends BusinessException {
+ public NotFoundException() {}
+
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+ public NotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/TechnicalException.java b/src/main/java/org/takiguchi/starter/exception/TechnicalException.java
new file mode 100644
index 0000000..976395b
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/TechnicalException.java
@@ -0,0 +1,23 @@
+package org.takiguchi.starter.exception;
+
+/**
+ * Technical exception.
+ */
+public class TechnicalException extends RuntimeException {
+ /**
+ * Constructs an exception with a message.
+ * @param message The description of the error met.
+ */
+ public TechnicalException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with a message and a code.
+ * @param message The description of the error met.
+ * @param cause The cause of the exception.
+ */
+ public TechnicalException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java b/src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java
new file mode 100644
index 0000000..4fbca71
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/TournamentValidationException.java
@@ -0,0 +1,7 @@
+package org.takiguchi.starter.exception;
+
+public class TournamentValidationException extends BusinessException {
+ public TournamentValidationException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java b/src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java
new file mode 100644
index 0000000..0dc5ec9
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/exception/UnauthorizedException.java
@@ -0,0 +1,22 @@
+package org.takiguchi.starter.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception thrown when an anonymous user attempt to access to secured resource or if he failed to login.
+ */
+@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
+public class UnauthorizedException extends BusinessException {
+ public UnauthorizedException() {
+ super();
+ }
+
+ public UnauthorizedException(String message) {
+ super(message);
+ }
+
+ public UnauthorizedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/takiguchi/starter/model/dao/User.java b/src/main/java/org/takiguchi/starter/model/dao/User.java
new file mode 100644
index 0000000..d70b806
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/model/dao/User.java
@@ -0,0 +1,26 @@
+package org.takiguchi.starter.model.dao;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import javax.persistence.*;
+import java.util.UUID;
+
+@Entity
+@Table(name = "`user`")
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class User {
+ @Id
+ @GeneratedValue(generator = "system-uuid")
+ private UUID id;
+ @Column(nullable = false)
+ private String email;
+ @Column(nullable = false)
+ private String password;
+ private String username;
+}
diff --git a/src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java b/src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java
new file mode 100644
index 0000000..361075b
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/model/dto/LoginRequest.java
@@ -0,0 +1,14 @@
+package org.takiguchi.starter.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter @Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class LoginRequest {
+ private String email;
+ private String password;
+}
diff --git a/src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java b/src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java
new file mode 100644
index 0000000..ea0ac67
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/model/dto/LoginResponse.java
@@ -0,0 +1,14 @@
+package org.takiguchi.starter.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class LoginResponse {
+ private String token;
+}
diff --git a/src/main/java/org/takiguchi/starter/repository/UserRepository.java b/src/main/java/org/takiguchi/starter/repository/UserRepository.java
new file mode 100644
index 0000000..5a03774
--- /dev/null
+++ b/src/main/java/org/takiguchi/starter/repository/UserRepository.java
@@ -0,0 +1,13 @@
+package org.takiguchi.starter.repository;
+
+import org.takiguchi.starter.model.dao.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+import java.util.UUID;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+ Optional findByEmail(String email);
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..303c1c5
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,36 @@
+app:
+ security:
+ signing-key: SigningKeyValue!
+ # 5 * 60 * 60 -> 5 hours
+ access-token-validity-seconds: 18000
+
+# =================================================
+# Spring configuration
+# =================================================
+spring:
+ # -------------------------------------------------
+ # Database configuration
+ # -------------------------------------------------
+ datasource:
+ driverClassName: org.postgresql.Driver
+ url: jdbc:postgresql://localhost:5432/db_database
+ username: postgres
+ password: postgres
+ # Disable feature detection by this undocumented parameter.
+ # Check the org.hibernate.engine.jdbc.internal.JdbcServiceImpl.configure method for more details.
+ jpa:
+ database-platform: org.hibernate.dialect.PostgreSQLDialect
+ properties.hibernate.temp.use_jdbc_metadata_defaults: false
+ open-in-view: false
+
+server:
+ error:
+ whitelabel:
+ enabled: false # Disable html error responses.
+
+logging:
+ level:
+ org:
+ # hibernate: DEBUG
+ springframework:
+# mail: DEBUG
diff --git a/src/test/java/org/takiguchi/starter/StarterApplicationTests.java b/src/test/java/org/takiguchi/starter/StarterApplicationTests.java
new file mode 100644
index 0000000..0d66298
--- /dev/null
+++ b/src/test/java/org/takiguchi/starter/StarterApplicationTests.java
@@ -0,0 +1,13 @@
+package org.takiguchi.starter;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class StarterApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}