Added project
This commit is contained in:
13
src/main/java/ru/resprojects/restsrv/RestsrvApplication.java
Normal file
13
src/main/java/ru/resprojects/restsrv/RestsrvApplication.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package ru.resprojects.restsrv;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class RestsrvApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RestsrvApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
17
src/main/java/ru/resprojects/restsrv/config/AppConfig.java
Normal file
17
src/main/java/ru/resprojects/restsrv/config/AppConfig.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package ru.resprojects.restsrv.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.resprojects.restsrv.token.TokenResourceDetails;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("auth")
|
||||
public TokenResourceDetails tokenResourceDetails() {
|
||||
return new TokenResourceDetails();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ru.resprojects.restsrv.config;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Component
|
||||
public class TokenAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7782026919358529193L;
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException) throws IOException, ServletException {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ru.resprojects.restsrv.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import ru.resprojects.restsrv.service.AuthDetailsService;
|
||||
import ru.resprojects.restsrv.token.TokenResourceDetails;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class TokenRequestsFilter extends OncePerRequestFilter {
|
||||
|
||||
public static final String SECURITY_SCHEME = "Bearer";
|
||||
private final AuthDetailsService authDetailsService;
|
||||
private final TokenResourceDetails tokenResourceDetails;
|
||||
|
||||
public TokenRequestsFilter(AuthDetailsService authDetailsService, TokenResourceDetails tokenResourceDetails) {
|
||||
this.authDetailsService = authDetailsService;
|
||||
this.tokenResourceDetails = tokenResourceDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
final String requestHeader = request.getHeader("Authorization");
|
||||
String token = null;
|
||||
if (requestHeader != null && requestHeader.startsWith(SECURITY_SCHEME + " ")) {
|
||||
token = requestHeader.substring(SECURITY_SCHEME.length() + 1);
|
||||
}
|
||||
if (token != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
UserDetails userDetails = authDetailsService.loadUserByUsername(tokenResourceDetails.getUserByToken(token));
|
||||
UsernamePasswordAuthenticationToken usernameAuthToken = new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities());
|
||||
usernameAuthToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(usernameAuthToken);
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package ru.resprojects.restsrv.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
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.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import ru.resprojects.restsrv.service.AuthDetailsService;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private static final String[] AUTH_WHITELIST = {
|
||||
// swagger ui
|
||||
"/v3/api-docs/**",
|
||||
"/swagger-ui.html",
|
||||
"/swagger-ui/**"
|
||||
};
|
||||
|
||||
private final TokenRequestsFilter tokenRequestsFilter;
|
||||
private final AuthDetailsService authDetailsService;
|
||||
private final TokenAuthenticationEntryPoint tokenAuthenticationEntryPoint;
|
||||
|
||||
public WebSecurityConfig(
|
||||
TokenRequestsFilter tokenRequestsFilter,
|
||||
AuthDetailsService authDetailsService, TokenAuthenticationEntryPoint tokenAuthenticationEntryPoint) {
|
||||
this.tokenRequestsFilter = tokenRequestsFilter;
|
||||
this.authDetailsService = authDetailsService;
|
||||
this.tokenAuthenticationEntryPoint = tokenAuthenticationEntryPoint;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(authDetailsService).passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.cors().and().csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers(AUTH_WHITELIST)
|
||||
.permitAll()
|
||||
.antMatchers("/**")
|
||||
.authenticated()
|
||||
.and()
|
||||
.exceptionHandling().authenticationEntryPoint(tokenAuthenticationEntryPoint)
|
||||
.and()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
http.addFilterBefore(tokenRequestsFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package ru.resprojects.restsrv.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.resprojects.restsrv.exception.LastException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/error")
|
||||
@Schema(name = "/error")
|
||||
@Tag(name ="Profiles", description = "the profiles API with documentation annotations")
|
||||
@SecurityScheme(
|
||||
name = "customTokenAuth",
|
||||
type = SecuritySchemeType.HTTP,
|
||||
in = SecuritySchemeIn.HEADER,
|
||||
scheme = "bearer",
|
||||
description = "Авторизация при помощи токена доступа. В HEADER запроса должна присутствовать строка вида Authorization: Bearer "
|
||||
)
|
||||
public class ErrorController {
|
||||
|
||||
@Operation(
|
||||
summary = "Получить последнюю ошибку",
|
||||
description = "Возвращает информацию о последней ошибке.",
|
||||
security = @SecurityRequirement(name = "customTokenAuth"),
|
||||
tags = { "profile" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(schema = @Schema(implementation = LastException.class), mediaType = MediaType.APPLICATION_JSON_VALUE)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/last", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<LastException> getLastException() {
|
||||
return ResponseEntity.ok(RestExceptionHandler.getLastException());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package ru.resprojects.restsrv.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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 ru.resprojects.restsrv.dto.ProfileDto;
|
||||
import ru.resprojects.restsrv.dto.ProfileIdDto;
|
||||
import ru.resprojects.restsrv.dto.EmailDto;
|
||||
import ru.resprojects.restsrv.exception.ErrorMessage;
|
||||
import ru.resprojects.restsrv.model.Profile;
|
||||
import ru.resprojects.restsrv.service.ProfileService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/profiles")
|
||||
@Schema(name = "/profiles")
|
||||
@Tag(name ="Profiles", description = "the profiles API with documentation annotations")
|
||||
@SecurityScheme(
|
||||
name = "customTokenAuth",
|
||||
type = SecuritySchemeType.HTTP,
|
||||
in = SecuritySchemeIn.HEADER,
|
||||
scheme = "bearer",
|
||||
description = "Авторизация при помощи токена доступа. В HEADER запроса должна присутствовать строка вида Authorization: Bearer "
|
||||
)
|
||||
public class ProfileController {
|
||||
|
||||
private final ProfileService profileService;
|
||||
|
||||
public ProfileController(ProfileService profileService) {
|
||||
this.profileService = profileService;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Получить список профилей",
|
||||
description = "Возвращает все созданные профили",
|
||||
security = @SecurityRequirement(name = "customTokenAuth"),
|
||||
tags = { "profile" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
array = @ArraySchema(schema = @Schema(implementation = Profile.class)),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<List<Profile>> finaAll() {
|
||||
return ResponseEntity.ok(profileService.findAll());
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Поиск профиля по ID",
|
||||
description = "Возвращает профиль с заданным ID",
|
||||
security = @SecurityRequirement(name = "customTokenAuth"),
|
||||
tags = { "profile" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Успешная операция",
|
||||
content = @Content(schema = @Schema(implementation = Profile.class), mediaType = MediaType.APPLICATION_JSON_VALUE)),
|
||||
@ApiResponse(responseCode = "404", description = "Профиль не найден",
|
||||
content = @Content(schema = @Schema(implementation = ErrorMessage.class), mediaType = MediaType.APPLICATION_JSON_VALUE)
|
||||
) })
|
||||
@GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Profile> findById(
|
||||
@Parameter(description="Id профиля.", required=true)
|
||||
@PathVariable("id") Integer id) {
|
||||
Profile profile = profileService.findById(id);
|
||||
return ResponseEntity.ok(profile);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Поиск профиля по email",
|
||||
description = "Возвращает профиль по указанному email",
|
||||
security = @SecurityRequirement(name = "customTokenAuth"),
|
||||
tags = { "profile" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Успешная операция",
|
||||
content = @Content(schema = @Schema(implementation = Profile.class), mediaType = MediaType.APPLICATION_JSON_VALUE)),
|
||||
@ApiResponse(responseCode = "404", description = "Профиль не найден",
|
||||
content = @Content(schema = @Schema(implementation = ErrorMessage.class), mediaType = MediaType.APPLICATION_JSON_VALUE))
|
||||
})
|
||||
@PostMapping(value = "/get", consumes = {MediaType.APPLICATION_JSON_VALUE})
|
||||
public ResponseEntity<Profile> findByEmail(
|
||||
@Parameter(description="Email по которому необходимо произвести поиск.",
|
||||
required=true, schema=@Schema(implementation = EmailDto.class))
|
||||
@RequestBody EmailDto emailDto) {
|
||||
Profile profile = profileService.findByEmail(emailDto.getEmail());
|
||||
return ResponseEntity.ok(profile);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Получить последний созданный профиль",
|
||||
description = "Возвращает последний созданный профиль",
|
||||
security = @SecurityRequirement(name = "customTokenAuth"),
|
||||
tags = { "profile" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(schema = @Schema(implementation = Profile.class), mediaType = MediaType.APPLICATION_JSON_VALUE)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/last", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Profile> getLastProfile() {
|
||||
Profile profile = profileService.getLastCreatedProfile();
|
||||
return ResponseEntity.ok(profile);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Создание нового профиля",
|
||||
description = "Создает профиль и возвращается его id",
|
||||
security = @SecurityRequirement(name = "customTokenAuth"),
|
||||
tags = { "profile" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Успешная операция",
|
||||
content = @Content(schema = @Schema(implementation = ProfileIdDto.class), mediaType = MediaType.APPLICATION_JSON_VALUE)),
|
||||
@ApiResponse(responseCode = "400", description = "Передан некорректный email добавляемого профиля",
|
||||
content = @Content(schema = @Schema(implementation = ErrorMessage.class), mediaType = MediaType.APPLICATION_JSON_VALUE)),
|
||||
@ApiResponse(responseCode = "403", description = "Email добавляемого профиля уже существует",
|
||||
content = @Content(schema = @Schema(implementation = ErrorMessage.class), mediaType = MediaType.APPLICATION_JSON_VALUE))
|
||||
})
|
||||
@PostMapping(value = "/set", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ProfileIdDto> save(
|
||||
@Parameter(description="Данные нового профиля",
|
||||
required=true, schema=@Schema(implementation = ProfileDto.class))
|
||||
@RequestBody ProfileDto profileDto) {
|
||||
Profile profile = new Profile();
|
||||
profile.setName(profileDto.getName());
|
||||
profile.setAge(profileDto.getAge());
|
||||
profile.setEmail(profileDto.getEmail());
|
||||
Profile newProfile = profileService.save(profile);
|
||||
ProfileIdDto profileIdDto = new ProfileIdDto(newProfile.getId());
|
||||
return ResponseEntity.ok(profileIdDto);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ru.resprojects.restsrv.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import ru.resprojects.restsrv.exception.BadResourceException;
|
||||
import ru.resprojects.restsrv.exception.ErrorMessage;
|
||||
import ru.resprojects.restsrv.exception.LastException;
|
||||
import ru.resprojects.restsrv.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.restsrv.exception.ResourceNotFoundException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class RestExceptionHandler {
|
||||
|
||||
private static LastException lastException;
|
||||
|
||||
@ExceptionHandler(value = {ResourceNotFoundException.class})
|
||||
public ResponseEntity<ErrorMessage> resourceNotFound(HttpServletRequest request, ResourceNotFoundException exception) {
|
||||
setLastException(exception.getErrorMessage());
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {BadResourceException.class})
|
||||
public ResponseEntity<ErrorMessage> badResource(HttpServletRequest request, BadResourceException exception) {
|
||||
setLastException(exception.getErrorMessage());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {ResourceAlreadyExistsException.class})
|
||||
public ResponseEntity<ErrorMessage> alreadyExistResource(HttpServletRequest request, ResourceAlreadyExistsException exception) {
|
||||
setLastException(exception.getErrorMessage());
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
private static void setLastException(ErrorMessage errorMessage) {
|
||||
lastException = new LastException(errorMessage.getMessage(), new Timestamp(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public static LastException getLastException() {
|
||||
return lastException;
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/java/ru/resprojects/restsrv/dto/EmailDto.java
Normal file
20
src/main/java/ru/resprojects/restsrv/dto/EmailDto.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ru.resprojects.restsrv.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EmailDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5708728999488347598L;
|
||||
|
||||
@Schema(description = "Email для поиска профиля")
|
||||
private String email;
|
||||
|
||||
public EmailDto() {
|
||||
}
|
||||
}
|
||||
33
src/main/java/ru/resprojects/restsrv/dto/ProfileDto.java
Normal file
33
src/main/java/ru/resprojects/restsrv/dto/ProfileDto.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package ru.resprojects.restsrv.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ProfileDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8315646868618789737L;
|
||||
|
||||
@Schema(description = "Имя пользователя",
|
||||
example = "Alex", required = false)
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "E-mail пользователя",
|
||||
example = "user@example.com", required = true)
|
||||
@JsonProperty("email")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "Возраст пользователя",
|
||||
example = "30", required = false)
|
||||
@JsonProperty("age")
|
||||
private Integer age;
|
||||
|
||||
public ProfileDto() {
|
||||
}
|
||||
}
|
||||
24
src/main/java/ru/resprojects/restsrv/dto/ProfileIdDto.java
Normal file
24
src/main/java/ru/resprojects/restsrv/dto/ProfileIdDto.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package ru.resprojects.restsrv.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class ProfileIdDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7924116702230840180L;
|
||||
|
||||
@Schema(description = "ID созданного профиля")
|
||||
@JsonProperty("idUser")
|
||||
private Integer userId;
|
||||
|
||||
public ProfileIdDto() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.resprojects.restsrv.exception;
|
||||
|
||||
public class BadResourceException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 5658497082104293714L;
|
||||
|
||||
public BadResourceException() {
|
||||
}
|
||||
|
||||
public BadResourceException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.resprojects.restsrv.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class ErrorMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -814379271893846783L;
|
||||
|
||||
@Schema(description = "Сообщение о ошибке")
|
||||
@JsonProperty("msg")
|
||||
private String message;
|
||||
|
||||
public ErrorMessage() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.resprojects.restsrv.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class LastException implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1153271692647620929L;
|
||||
|
||||
@Schema(description = "Сообщение о ошибки")
|
||||
@JsonProperty("msg")
|
||||
private String message;
|
||||
@Schema(description = "Дата и время возникновения ошибки")
|
||||
@JsonProperty("created")
|
||||
private Timestamp created;
|
||||
|
||||
public LastException() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.resprojects.restsrv.exception;
|
||||
|
||||
public class ResourceAlreadyExistsException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -2318810158739700082L;
|
||||
|
||||
public ResourceAlreadyExistsException() {
|
||||
}
|
||||
|
||||
public ResourceAlreadyExistsException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.resprojects.restsrv.exception;
|
||||
|
||||
public class ResourceNotFoundException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -4368442279968350909L;
|
||||
|
||||
public ResourceNotFoundException() {
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
79
src/main/java/ru/resprojects/restsrv/model/Profile.java
Normal file
79
src/main/java/ru/resprojects/restsrv/model/Profile.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package ru.resprojects.restsrv.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Entity
|
||||
@Table(name = "profile")
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class Profile implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4048798961366546485L;
|
||||
private static final int START_SEQ = 5000;
|
||||
|
||||
@Schema(description = "Unique identifier of the Profile.",
|
||||
example = "1", required = true)
|
||||
@Id
|
||||
@SequenceGenerator(name = "global_seq", sequenceName = "global_seq",
|
||||
allocationSize = 1, initialValue = START_SEQ)
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "global_seq")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "Имя пользователя",
|
||||
example = "Alex", required = false)
|
||||
private String name;
|
||||
|
||||
@Schema(description = "E-mail пользователя",
|
||||
example = "user@example.com", required = true)
|
||||
private String email;
|
||||
|
||||
@Schema(description = "Возраст пользователя",
|
||||
example = "30", required = false)
|
||||
private Integer age;
|
||||
|
||||
@Schema(description = "Дата и время создания профиля",
|
||||
example = "2020-08-24T10:16:17.929+00:00", required = false)
|
||||
private Timestamp created;
|
||||
|
||||
public Profile() {
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
if (created == null) {
|
||||
created = new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Profile profile = (Profile) o;
|
||||
|
||||
return id.equals(profile.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.resprojects.restsrv.repository;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.resprojects.restsrv.model.Profile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ProfileRepository extends CrudRepository<Profile, Integer> {
|
||||
|
||||
List<Profile> findByEmailContainingIgnoreCase(String email);
|
||||
|
||||
Boolean existsByEmailContainingIgnoreCase(String email);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.resprojects.restsrv.service;
|
||||
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Service
|
||||
public class AuthDetailsService implements UserDetailsService {
|
||||
|
||||
private static final String INMEMORY_USER = "user";
|
||||
private static final String INMEMORY_PASSWORD = "$2y$12$jj9Q40qh3NOokcPjIg2cFuFK/7jBZlZ/RcrEbXkOALRv88hcuLF5a";
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
if (INMEMORY_USER.equals(username)) {
|
||||
return new User(INMEMORY_USER, INMEMORY_PASSWORD, new ArrayList<>());
|
||||
} else {
|
||||
throw new UsernameNotFoundException("Authorization error! User " + username + " not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ru.resprojects.restsrv.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.resprojects.restsrv.exception.BadResourceException;
|
||||
import ru.resprojects.restsrv.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.restsrv.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.restsrv.model.Profile;
|
||||
import ru.resprojects.restsrv.repository.ProfileRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static ru.resprojects.restsrv.util.ValidationUtil.isEmailValid;
|
||||
|
||||
@Service
|
||||
public class ProfileService {
|
||||
|
||||
private final ProfileRepository repository;
|
||||
private Profile lastCreatedProfile;
|
||||
|
||||
public ProfileService(ProfileRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public Profile findById(Integer id) throws ResourceNotFoundException {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Cannot find profile with id: " + id));
|
||||
}
|
||||
|
||||
public List<Profile> findAll() {
|
||||
List<Profile> profiles = new ArrayList<>();
|
||||
repository.findAll().forEach(profiles::add);
|
||||
return profiles;
|
||||
}
|
||||
|
||||
public Profile findByEmail(String email) throws ResourceNotFoundException {
|
||||
List<Profile> profiles = repository.findByEmailContainingIgnoreCase(email);
|
||||
if (!profiles.isEmpty()) {
|
||||
return profiles.get(0);
|
||||
} else {
|
||||
throw new ResourceNotFoundException("Cannot find profile with email: " + email);
|
||||
}
|
||||
}
|
||||
|
||||
public Profile save(Profile profile) throws BadResourceException, ResourceAlreadyExistsException {
|
||||
if (profile == null) {
|
||||
throw new BadResourceException("Failed to save profile. Profile is null.");
|
||||
}
|
||||
String email = profile.getEmail();
|
||||
if (!isEmailValid(email)) {
|
||||
throw new BadResourceException("Failed to save profile. Email " + email + " is not valid.");
|
||||
}
|
||||
if (repository.existsByEmailContainingIgnoreCase(email)) {
|
||||
throw new ResourceAlreadyExistsException("Profile with email: " + profile.getEmail() + " already exists.");
|
||||
}
|
||||
lastCreatedProfile = repository.save(profile);
|
||||
return lastCreatedProfile;
|
||||
}
|
||||
|
||||
public Profile getLastCreatedProfile() {
|
||||
return lastCreatedProfile;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.resprojects.restsrv.token;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class TokenResourceDetails {
|
||||
|
||||
private String token;
|
||||
private String user;
|
||||
|
||||
public String getUserByToken(String token) {
|
||||
if (!this.token.equals(token)) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ru.resprojects.restsrv.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class ValidationUtil {
|
||||
|
||||
private ValidationUtil() {
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/48725527
|
||||
public static boolean isEmailValid(String email) {
|
||||
final Pattern EMAIL_REGEX = Pattern.compile(
|
||||
"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
return EMAIL_REGEX.matcher(email).matches();
|
||||
}
|
||||
|
||||
}
|
||||
65
src/main/resources/application.yml
Normal file
65
src/main/resources/application.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
server:
|
||||
port: 8010
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: pgsql
|
||||
---
|
||||
spring:
|
||||
profiles: pgsql, prod
|
||||
jpa:
|
||||
database: postgresql
|
||||
generate-ddl: true
|
||||
properties:
|
||||
hibernate:
|
||||
jdbc:
|
||||
lob:
|
||||
non_contextual_creation: true
|
||||
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
|
||||
open-in-view: false
|
||||
datasource:
|
||||
platform: postgresql
|
||||
initialization-mode: never
|
||||
---
|
||||
spring:
|
||||
profiles: pgsql
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost/test
|
||||
username: test
|
||||
password: test
|
||||
---
|
||||
spring:
|
||||
profiles: prod
|
||||
datasource:
|
||||
url: ${RESTSRV_PGSQL_DB_HOST}:${RESTSRV_PGSQL_DB_PORT}/${RESTSRV_PGSQL_DB_NAME}
|
||||
username: ${RESTSRV_PGSQL_DB_USER}
|
||||
password: ${RESTSRV_PGSQL_DB_PASSWORD}
|
||||
---
|
||||
spring:
|
||||
profiles: test, demo
|
||||
jpa:
|
||||
database: h2
|
||||
open-in-view: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
database-platform: org.hibernate.dialect.H2Dialect
|
||||
datasource:
|
||||
url: jdbc:h2:mem:restsrv;DB_CLOSE_ON_EXIT=FALSE
|
||||
initialization-mode: always
|
||||
platform: h2
|
||||
---
|
||||
logging:
|
||||
level:
|
||||
ru.resprojects: debug
|
||||
org.springframework.transaction: debug
|
||||
org.springframework: error
|
||||
pattern:
|
||||
file: "%d %p %c{1.} [%t] %m%n"
|
||||
console: "%clr(%d{HH:mm:ss.SSS}){yellow} %clr(%-5p) %clr(---){faint} %clr([%t]){cyan} %clr(%logger{36}){blue} %clr(:){red} %clr(%msg){faint}%n"
|
||||
file:
|
||||
name: restsrv.log
|
||||
max-size: 5MB
|
||||
|
||||
auth:
|
||||
token: secret
|
||||
user: user
|
||||
6
src/main/resources/data-h2.sql
Normal file
6
src/main/resources/data-h2.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
DELETE FROM profile;
|
||||
ALTER SEQUENCE global_seq RESTART WITH 5000;
|
||||
|
||||
INSERT INTO profile (name, email, age, created) VALUES
|
||||
('h2user1', 'h2user1@example.com', 10, now()),
|
||||
('h2user2', 'h2user2@example.com', 20, now());
|
||||
6
src/main/resources/data-postgresql.sql
Normal file
6
src/main/resources/data-postgresql.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
DELETE FROM profile;
|
||||
ALTER SEQUENCE global_seq RESTART WITH 5000;
|
||||
|
||||
INSERT INTO profile (name, email, age) VALUES
|
||||
('user1', 'user1@example.com', 20),
|
||||
('user2', 'user2@example.com', 30);
|
||||
13
src/main/resources/schema-h2.sql
Normal file
13
src/main/resources/schema-h2.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
DROP TABLE IF EXISTS profile;
|
||||
DROP SEQUENCE IF EXISTS global_seq;
|
||||
|
||||
CREATE SEQUENCE global_seq MINVALUE 5000;
|
||||
|
||||
CREATE TABLE profile (
|
||||
id INT DEFAULT global_seq.nextval PRIMARY KEY,
|
||||
name VARCHAR NOT NULL,
|
||||
email VARCHAR NOT NULL,
|
||||
age INT DEFAULT 0 NOT NULL,
|
||||
created TIMESTAMP
|
||||
);
|
||||
CREATE UNIQUE INDEX profile_unique_email_idx ON profile(email);
|
||||
13
src/main/resources/schema-postgresql.sql
Normal file
13
src/main/resources/schema-postgresql.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
DROP TABLE IF EXISTS profile;
|
||||
DROP SEQUENCE IF EXISTS global_seq CASCADE;
|
||||
|
||||
CREATE SEQUENCE global_seq START 5000;
|
||||
|
||||
CREATE TABLE profile (
|
||||
id INTEGER PRIMARY KEY DEFAULT nextval('global_seq'),
|
||||
name VARCHAR NOT NULL,
|
||||
email VARCHAR NOT NULL,
|
||||
age INTEGER DEFAULT 0 NOT NULL,
|
||||
created TIMESTAMP DEFAULT now()::timestamp
|
||||
);
|
||||
CREATE UNIQUE INDEX profile_unique_email_idx ON profile(email);
|
||||
44
src/test/java/ru/resprojects/restsrv/ValidationUtilTest.java
Normal file
44
src/test/java/ru/resprojects/restsrv/ValidationUtilTest.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package ru.resprojects.restsrv;
|
||||
|
||||
import org.junit.Test;
|
||||
import ru.resprojects.restsrv.util.ValidationUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ValidationUtilTest {
|
||||
|
||||
@Test
|
||||
public void whenPassCorrectEmailToValidatorThenReturnTrue() {
|
||||
List<String> emails = List.of(
|
||||
"john@somewhere.com",
|
||||
"john.foo@somewhere.com",
|
||||
"john.foo+label@somewhere.com",
|
||||
"john@192.168.1.10",
|
||||
"john+label@192.168.1.10",
|
||||
"john.foo@someserver",
|
||||
"JOHN.FOO@somewhere.com"
|
||||
);
|
||||
|
||||
for (String email : emails) {
|
||||
assertThat(ValidationUtil.isEmailValid(email)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPassIncorrectEmailToValidatorThenReturnFalse() {
|
||||
List<String> emails = List.of(
|
||||
"@someserver",
|
||||
"@someserver.com",
|
||||
"john@.",
|
||||
".@somewhere.com",
|
||||
".@.somewhere.com"
|
||||
);
|
||||
|
||||
for (String email : emails) {
|
||||
assertThat(ValidationUtil.isEmailValid(email)).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package ru.resprojects.restsrv.controller;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.restsrv.config.TokenRequestsFilter;
|
||||
import ru.resprojects.restsrv.dto.EmailDto;
|
||||
import ru.resprojects.restsrv.dto.ProfileDto;
|
||||
import ru.resprojects.restsrv.dto.ProfileIdDto;
|
||||
import ru.resprojects.restsrv.exception.ErrorMessage;
|
||||
import ru.resprojects.restsrv.exception.LastException;
|
||||
import ru.resprojects.restsrv.model.Profile;
|
||||
import ru.resprojects.restsrv.repository.ProfileRepository;
|
||||
import ru.resprojects.restsrv.token.TokenResourceDetails;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class ProfileControllerTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private ProfileRepository repository;
|
||||
|
||||
@Autowired
|
||||
private TokenResourceDetails tokenResourceDetails;
|
||||
|
||||
private Profile exampleProfile;
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
headers.add("Authorization", TokenRequestsFilter.SECURITY_SCHEME + " " + tokenResourceDetails.getToken());
|
||||
Profile profile = new Profile();
|
||||
profile.setName("Alex");
|
||||
profile.setEmail("alex@example.com");
|
||||
profile.setAge(10);
|
||||
exampleProfile = repository.save(profile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFindByExistentIdThenStatus200AndReturnProfile() {
|
||||
HttpEntity<String> entity = new HttpEntity<>(null, headers);
|
||||
|
||||
ResponseEntity<Profile> profile = restTemplate.exchange("/profiles/5000", HttpMethod.GET, entity, Profile.class);
|
||||
|
||||
assertThat(profile.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(profile.getBody()).isNotNull();
|
||||
assertThat(profile.getBody().getName()).isEqualTo(exampleProfile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestAllProfilesThenStatus200AndReturnListOfProfiles() {
|
||||
HttpEntity<String> entity = new HttpEntity<>(null, headers);
|
||||
|
||||
ResponseEntity<Profile[]> profiles = restTemplate.exchange("/profiles", HttpMethod.GET, entity, Profile[].class);
|
||||
|
||||
assertThat(profiles.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(profiles.getBody()).isNotNull();
|
||||
assertThat(profiles.getBody()).isNotEmpty();
|
||||
assertThat(profiles.getBody()[0].getName()).isEqualTo(exampleProfile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFindProfileByEmailThenStatus200AndReturnProfile() {
|
||||
EmailDto emailDto = new EmailDto();
|
||||
emailDto.setEmail("alex@example.com");
|
||||
HttpEntity<EmailDto> entity = new HttpEntity<>(emailDto, headers);
|
||||
|
||||
ResponseEntity<Profile> profile = restTemplate.exchange("/profiles/get", HttpMethod.POST, entity, Profile.class);
|
||||
|
||||
assertThat(profile.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(profile.getBody()).isNotNull();
|
||||
assertThat(profile.getBody().getName()).isEqualTo(exampleProfile.getName());
|
||||
assertThat(profile.getBody().getCreated()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreateProfileThenStatus200AndReturnIdSavedProfile() {
|
||||
ProfileDto profileDto = new ProfileDto();
|
||||
profileDto.setName("John");
|
||||
profileDto.setAge(10);
|
||||
profileDto.setEmail("john@gmail.com");
|
||||
HttpEntity<ProfileDto> entity = new HttpEntity<>(profileDto, headers);
|
||||
|
||||
ResponseEntity<ProfileIdDto> newProfileId = restTemplate.exchange("/profiles/set", HttpMethod.POST, entity, ProfileIdDto.class);
|
||||
|
||||
assertThat(newProfileId.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(newProfileId.getBody()).isNotNull();
|
||||
assertThat(newProfileId.getBody().getUserId()).isEqualTo(5001);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void whenRequestLastCreatedProfileThenStatus200AndReturnLastSavedProfile() {
|
||||
ProfileDto profileDto = new ProfileDto();
|
||||
profileDto.setName("John");
|
||||
profileDto.setAge(10);
|
||||
profileDto.setEmail("john@gmail.com");
|
||||
HttpEntity<ProfileDto> entity = new HttpEntity<>(profileDto, headers);
|
||||
|
||||
ResponseEntity<ProfileIdDto> newProfileId = restTemplate.exchange("/profiles/set", HttpMethod.POST, entity, ProfileIdDto.class);
|
||||
assertThat(newProfileId.getBody()).isNotNull();
|
||||
|
||||
HttpEntity<Void> newEntity = new HttpEntity<>(null, headers);
|
||||
ResponseEntity<Profile> profile = restTemplate.exchange("/profiles/last", HttpMethod.GET, newEntity, Profile.class);
|
||||
|
||||
assertThat(profile.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(profile.getBody()).isNotNull();
|
||||
assertThat(profile.getBody().getId()).isEqualTo(newProfileId.getBody().getUserId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestProfileWithNonexistentIdThenStatus404AndReturnErrorMessage() {
|
||||
HttpEntity<String> entity = new HttpEntity<>(null, headers);
|
||||
|
||||
ResponseEntity<ErrorMessage> errorMessage = restTemplate.exchange("/profiles/5010", HttpMethod.GET, entity, ErrorMessage.class);
|
||||
|
||||
assertThat(errorMessage.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(errorMessage.getBody()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestProfileWithNonexistentEmailThenStatus404AndReturnErrorMessage() {
|
||||
EmailDto emailDto = new EmailDto();
|
||||
emailDto.setEmail("test@example.com");
|
||||
HttpEntity<EmailDto> entity = new HttpEntity<>(emailDto, headers);
|
||||
|
||||
ResponseEntity<ErrorMessage> errorMessage = restTemplate.exchange("/profiles/get", HttpMethod.POST, entity, ErrorMessage.class);
|
||||
|
||||
assertThat(errorMessage.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(errorMessage.getBody()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatProfileWithIncorrectEmailThenReturnStatus400AndErrorMessage() {
|
||||
ProfileDto profileDto = new ProfileDto();
|
||||
profileDto.setName("John");
|
||||
profileDto.setAge(10);
|
||||
profileDto.setEmail(".@gmail.com");
|
||||
HttpEntity<ProfileDto> entity = new HttpEntity<>(profileDto, headers);
|
||||
|
||||
ResponseEntity<ErrorMessage> errorMessage = restTemplate.exchange("/profiles/set", HttpMethod.POST, entity, ErrorMessage.class);
|
||||
|
||||
assertThat(errorMessage.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(errorMessage.getBody()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatProfileWithExistentEmailThenReturnStatus403AndErrorMessage() {
|
||||
ProfileDto profileDto = new ProfileDto();
|
||||
profileDto.setName("John");
|
||||
profileDto.setAge(10);
|
||||
profileDto.setEmail("alex@example.com");
|
||||
HttpEntity<ProfileDto> entity = new HttpEntity<>(profileDto, headers);
|
||||
|
||||
ResponseEntity<ErrorMessage> errorMessage = restTemplate.exchange("/profiles/set", HttpMethod.POST, entity, ErrorMessage.class);
|
||||
|
||||
assertThat(errorMessage.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
assertThat(errorMessage.getBody()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestLastErrorThenStatus200AndReturnMessageWithLastError() {
|
||||
HttpEntity<String> entity = new HttpEntity<>(null, headers);
|
||||
ResponseEntity<ErrorMessage> errorMessage = restTemplate.exchange("/profiles/5010", HttpMethod.GET, entity, ErrorMessage.class);
|
||||
assertThat(errorMessage.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(errorMessage.getBody()).isNotNull();
|
||||
|
||||
ResponseEntity<LastException> lastErrorMessage = restTemplate.exchange("/error/last", HttpMethod.GET, entity, LastException.class);
|
||||
assertThat(lastErrorMessage.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(lastErrorMessage.getBody()).isNotNull();
|
||||
assertThat(lastErrorMessage.getBody().getMessage()).isEqualTo(errorMessage.getBody().getMessage());
|
||||
assertThat(lastErrorMessage.getBody().getCreated()).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package ru.resprojects.restsrv.service;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.restsrv.RestsrvApplication;
|
||||
import ru.resprojects.restsrv.exception.BadResourceException;
|
||||
import ru.resprojects.restsrv.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.restsrv.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.restsrv.model.Profile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = RestsrvApplication.class)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class ProfileServiceTest {
|
||||
|
||||
@Autowired
|
||||
private ProfileService profileService;
|
||||
|
||||
private Profile exampleProfile;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
Profile profile = new Profile();
|
||||
profile.setName("h2user1");
|
||||
profile.setEmail("h2user1@example.com");
|
||||
profile.setAge(20);
|
||||
exampleProfile = profileService.save(profile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSaveNewProfileThenReturnedProfileWithId() {
|
||||
Profile exampleProfile = new Profile();
|
||||
exampleProfile.setName("Alex");
|
||||
exampleProfile.setEmail("alex@example.com");
|
||||
exampleProfile.setAge(20);
|
||||
|
||||
Profile newProfile = profileService.save(exampleProfile);
|
||||
|
||||
assertThat(newProfile).isNotNull();
|
||||
assertThat(newProfile.getId()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFindByIdThenReturnedProfile() {
|
||||
Profile profile = profileService.findById(5000);
|
||||
|
||||
assertThat(profile).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFindByEmailThenReturnedProfile() {
|
||||
Profile profile = profileService.findByEmail("h2user1@example.com");
|
||||
|
||||
assertThat(profile).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFindByEmailCaseInsensitiveThenReturnedProfile() {
|
||||
Profile profile = profileService.findByEmail("H2uSer1@eXAmple.com");
|
||||
|
||||
assertThat(profile).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetLastCreatedProfileThenReturnedLastSavedProfile() {
|
||||
Profile profile = profileService.getLastCreatedProfile();
|
||||
|
||||
assertThat(profile).isNotNull();
|
||||
assertThat(profile).isEqualTo(exampleProfile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFindAllThenReturnedAllProfiles() {
|
||||
List<Profile> profiles = profileService.findAll();
|
||||
|
||||
assertThat(profiles).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenFindByNonexistentIdWhenException() {
|
||||
profileService.findById(5010);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenFindByNonexistentEmailThenException() {
|
||||
profileService.findByEmail("test@test.com");
|
||||
}
|
||||
|
||||
@Test(expected = BadResourceException.class)
|
||||
public void whenSaveProfileWithIncorrectEmailThenException() {
|
||||
Profile exampleProfile = new Profile();
|
||||
exampleProfile.setName("Alex");
|
||||
exampleProfile.setEmail(".@example.com");
|
||||
exampleProfile.setAge(20);
|
||||
|
||||
profileService.save(exampleProfile);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceAlreadyExistsException.class)
|
||||
public void whenSaveProfileWithExistEmailThenException() {
|
||||
Profile exampleProfile = new Profile();
|
||||
exampleProfile.setName("Alex");
|
||||
exampleProfile.setEmail("h2user1@example.com");
|
||||
exampleProfile.setAge(20);
|
||||
|
||||
profileService.save(exampleProfile);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceAlreadyExistsException.class)
|
||||
public void whenSaveProfileWithExistEmailCaseInsensitiveThenException() {
|
||||
Profile exampleProfile = new Profile();
|
||||
exampleProfile.setName("Alex");
|
||||
exampleProfile.setEmail("H2user1@example.com");
|
||||
exampleProfile.setAge(20);
|
||||
|
||||
profileService.save(exampleProfile);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user