Skip to content

Commit

Permalink
TEMPLATE-5: Реализован импорт шаблонов
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrvsrg committed Mar 29, 2024
1 parent 2993003 commit 565864b
Show file tree
Hide file tree
Showing 34 changed files with 1,275 additions and 8 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ fabric.properties
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

Expand Down
47 changes: 46 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand All @@ -22,6 +22,12 @@
<properties>
<java.version>17</java.version>
<common-auth.version>0.0.0</common-auth.version>
<mapstruct.version>1.5.2.Final</mapstruct.version>
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
<lombok.version>1.18.20</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
<json-schema-validator.version>1.4.0</json-schema-validator.version>
<commons-io.version>2.15.1</commons-io.version>
</properties>

<profiles>
Expand Down Expand Up @@ -107,6 +113,21 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>


<!-- Testing -->
<dependency>
Expand Down Expand Up @@ -135,6 +156,30 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.archipio.templateservice.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

@Configuration
public class LocaleConfig {

@Bean
public LocaleResolver localeResolver() {
return new AcceptHeaderLocaleResolver();
}

@Bean
public ResourceBundleMessageSource messageSource() {
final ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setDefaultEncoding("UTF-8");
source.setBasename("lang/messages");
return source;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.archipio.templateservice.config;

import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;

import com.archipio.commonauth.JwtTokenProvider;
import com.archipio.commonauth.JwtVerifierFilter;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableMethodSecurity
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.sessionManagement((session) -> session.sessionCreationPolicy(STATELESS))
.cors((cors) -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(request -> request.anyRequest().permitAll())
.addFilterBefore(
new JwtVerifierFilter(new JwtTokenProvider()),
UsernamePasswordAuthenticationFilter.class)
.build();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

@Bean
public UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package com.archipio.templateservice.controller;

import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.METHOD_NOT_ALLOWED;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.UNSUPPORTED_MEDIA_TYPE;

import com.archipio.templateservice.dto.ErrorDto;
import com.archipio.templateservice.exception.InvalidTemplateConfigurationFormatException;
import com.archipio.templateservice.exception.InvalidZipFormatException;
import com.archipio.templateservice.exception.TemplateCodeAlreadyExistsException;
import com.archipio.templateservice.exception.TemplateNameAlreadyExistsException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import java.time.Instant;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.support.RequestContextUtils;

@RestControllerAdvice
@RequiredArgsConstructor
public class ExceptionCatcher {

private final MessageSource messageSource;

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorDto> handleMethodArgumentNotValidException(
HttpServletRequest request, MethodArgumentNotValidException e) {
var errors =
e.getBindingResult().getFieldErrors().stream()
.collect(
Collectors.groupingBy(
FieldError::getField,
Collectors.mapping(FieldError::getDefaultMessage, Collectors.joining(" "))));

return ResponseEntity.status(BAD_REQUEST)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.validation-error", request))
.errors(errors)
.build());
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorDto> handleConstraintViolationException(
HttpServletRequest request, ConstraintViolationException e) {
var errors =
e.getConstraintViolations().stream()
.map(
constraintViolation ->
new FieldError(
constraintViolation.getRootBeanClass().getName(),
constraintViolation.getPropertyPath().toString(),
constraintViolation.getMessage()))
.collect(
Collectors.groupingBy(
FieldError::getField,
Collectors.mapping(FieldError::getDefaultMessage, Collectors.joining(" "))));

return ResponseEntity.status(BAD_REQUEST)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.validation-error", request))
.errors(errors)
.build());
}

@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<ErrorDto> handleNoHandlerFoundException(HttpServletRequest request) {
return ResponseEntity.status(NOT_FOUND)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.endpoint-not-found", request))
.build());
}

@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorDto> handleHttpRequestMethodNotSupportedException(
HttpServletRequest request) {
return ResponseEntity.status(METHOD_NOT_ALLOWED)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.method-not-supported", request))
.build());
}

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ErrorDto> handleHttpMessageNotReadableException(
HttpServletRequest request) {
return ResponseEntity.status(BAD_REQUEST)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.invalid-json-format", request))
.build());
}

@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<ErrorDto> handleMissingServletRequestParameterException(
HttpServletRequest request) {
return ResponseEntity.status(BAD_REQUEST)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.missing-request-parameter", request))
.build());
}

@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public ResponseEntity<ErrorDto> handleHttpMediaTypeNotSupportedException(
HttpServletRequest request) {
return ResponseEntity.status(UNSUPPORTED_MEDIA_TYPE)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.unsupported-media-type", request))
.build());
}

@ExceptionHandler(InvalidZipFormatException.class)
public ResponseEntity<ErrorDto> handleUnsupportedZipFormatException(
HttpServletRequest request, InvalidZipFormatException e) {
var joiner = new StringJoiner(" ");
joiner.add(getMessage("exception.invalid-zip-format", request));
joiner.add(e.getMessage());

return ResponseEntity.status(BAD_REQUEST)
.body(ErrorDto.builder().createdAt(Instant.now()).message(joiner.toString()).build());
}

@ExceptionHandler(InvalidTemplateConfigurationFormatException.class)
public ResponseEntity<ErrorDto> handleInvalidTemplateConfigurationFormatException(
HttpServletRequest request) {
return ResponseEntity.status(BAD_REQUEST)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.invalid-template-config-format", request))
.build());
}

@ExceptionHandler(TemplateCodeAlreadyExistsException.class)
public ResponseEntity<ErrorDto> handleEmailAlreadyExistsException(HttpServletRequest request) {
return ResponseEntity.status(CONFLICT)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.template-code-already-exists", request))
.build());
}

@ExceptionHandler(TemplateNameAlreadyExistsException.class)
public ResponseEntity<ErrorDto> handleTemplateNameAlreadyExistsException(
HttpServletRequest request) {
return ResponseEntity.status(CONFLICT)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.template-name-already-exists", request))
.build());
}

@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorDto> handleAccessDeniedException(HttpServletRequest request) {
return ResponseEntity.status(FORBIDDEN)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.access-denied", request))
.build());
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDto> handleException(HttpServletRequest request) {
return ResponseEntity.status(INTERNAL_SERVER_ERROR)
.body(
ErrorDto.builder()
.createdAt(Instant.now())
.message(getMessage("exception.internal-server-error", request))
.build());
}

private String getMessage(String code, HttpServletRequest request) {
return messageSource.getMessage(code, null, RequestContextUtils.getLocale(request));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.archipio.templateservice.controller.api.v0;

import static com.archipio.templateservice.util.ApiUtils.API_V0_PREFIX;
import static com.archipio.templateservice.util.ApiUtils.IMPORT_SUFFIX;
import static org.springframework.http.HttpStatus.OK;

import com.archipio.templateservice.service.TemplateService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequiredArgsConstructor
@RequestMapping(API_V0_PREFIX)
public class TemplateController {

private final TemplateService templateService;

@PostMapping(value = IMPORT_SUFFIX, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> importTemplate(@RequestPart("data") MultipartFile data) {
templateService.importTemplate(data);
return ResponseEntity.status(OK).build();
}
}
Loading

0 comments on commit 565864b

Please sign in to comment.