From 392b99aca4d9b1fbc905a34932346bbb214aff2a Mon Sep 17 00:00:00 2001 From: etreschenkov Date: Tue, 1 Jul 2025 15:47:45 +0300 Subject: [PATCH] new exception --- .../config/rest/GlobalExceptionHandler.java | 36 +++++- .../entities/dto/SubjectData.java | 2 + .../credit_tracker/enums/EnumKeyMessage.java | 41 +++++++ .../credit_tracker/enums/IEnumKeyCode.java | 102 +++++++++++++++++ .../credit_tracker/enums/SignalError.java | 17 ++- .../credit_tracker/enums/XmlErrorFormat.java | 10 ++ .../exceptions/SignalClientException.java | 18 +++ .../exceptions/SignalErrorException.java | 4 +- .../credit_tracker/facade/SignalFacade.java | 105 ++++++++---------- .../service/xml/jaxb/XmlUtilService.java | 1 + .../nbch/credit_tracker/utils/ZipUtils.java | 28 +++++ .../utils/error/ExceptionUtils.java | 15 +++ .../utils/error/SimpleMessageResolver.java | 12 ++ 13 files changed, 325 insertions(+), 66 deletions(-) create mode 100644 src/main/java/ru/nbch/credit_tracker/enums/EnumKeyMessage.java create mode 100644 src/main/java/ru/nbch/credit_tracker/enums/IEnumKeyCode.java create mode 100644 src/main/java/ru/nbch/credit_tracker/enums/XmlErrorFormat.java create mode 100644 src/main/java/ru/nbch/credit_tracker/exceptions/SignalClientException.java create mode 100644 src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java create mode 100644 src/main/java/ru/nbch/credit_tracker/utils/error/ExceptionUtils.java create mode 100644 src/main/java/ru/nbch/credit_tracker/utils/error/SimpleMessageResolver.java diff --git a/src/main/java/ru/nbch/credit_tracker/config/rest/GlobalExceptionHandler.java b/src/main/java/ru/nbch/credit_tracker/config/rest/GlobalExceptionHandler.java index 3ce661b..c37e324 100644 --- a/src/main/java/ru/nbch/credit_tracker/config/rest/GlobalExceptionHandler.java +++ b/src/main/java/ru/nbch/credit_tracker/config/rest/GlobalExceptionHandler.java @@ -1,5 +1,6 @@ package ru.nbch.credit_tracker.config.rest; +import java.nio.charset.Charset; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -7,13 +8,19 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import ru.nbch.credit_tracker.entities.response.errors.CommonError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringActivePackagesError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringDownloadError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringPackageDeleteError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringReportListError; +import ru.nbch.credit_tracker.exceptions.SignalClientException; import ru.nbch.credit_tracker.exceptions.SignalErrorException; - -import java.nio.charset.Charset; +import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver; @RestControllerAdvice public class GlobalExceptionHandler { + private final SimpleMessageResolver messageResolver = new SimpleMessageResolver(); @ExceptionHandler(value = SignalErrorException.class, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity handleInvalidUser(SignalErrorException ex) { HttpHeaders headers = new HttpHeaders(); @@ -24,4 +31,29 @@ public class GlobalExceptionHandler { return new ResponseEntity<>(ex.getError(), headers, HttpStatus.INTERNAL_SERVER_ERROR); } } + + @ExceptionHandler(value = SignalClientException.class, produces = MediaType.APPLICATION_XML_VALUE) + public ResponseEntity handleClientException(SignalClientException ex) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(new MediaType("application", "xml", Charset.forName("windows-1251"))); + CommonError commonError = getCommonError(ex); + commonError.setStatus("error"); + commonError.setErrorCode(ex.getErrorMessage().getSubject().getErrorCode()); + commonError.setErrorText(messageResolver.resolve(ex.getErrorMessage())); + + return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST); + } + + private static CommonError getCommonError(SignalClientException ex) { + CommonError commonError; + switch (ex.getErrorFormat()) { + case MonitoringError -> commonError = new MonitoringError(); + case MonitoringActivePackagesError -> commonError = new MonitoringActivePackagesError(); + case MonitoringDownloadError -> commonError = new MonitoringDownloadError(); + case MonitoringReportListError -> commonError = new MonitoringReportListError(); + case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError(); + default -> throw new IllegalStateException("Unsupported error format: " + ex.getErrorFormat()); + } + return commonError; + } } diff --git a/src/main/java/ru/nbch/credit_tracker/entities/dto/SubjectData.java b/src/main/java/ru/nbch/credit_tracker/entities/dto/SubjectData.java index 95e1cd9..0dca665 100644 --- a/src/main/java/ru/nbch/credit_tracker/entities/dto/SubjectData.java +++ b/src/main/java/ru/nbch/credit_tracker/entities/dto/SubjectData.java @@ -1,6 +1,7 @@ package ru.nbch.credit_tracker.entities.dto; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -11,6 +12,7 @@ import java.time.LocalDateTime; @Setter @AllArgsConstructor @NoArgsConstructor +@Builder public class SubjectData { private Integer id; private Integer packageId; diff --git a/src/main/java/ru/nbch/credit_tracker/enums/EnumKeyMessage.java b/src/main/java/ru/nbch/credit_tracker/enums/EnumKeyMessage.java new file mode 100644 index 0000000..5c9e30f --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/enums/EnumKeyMessage.java @@ -0,0 +1,41 @@ +package ru.nbch.credit_tracker.enums; + +import java.util.Arrays; +import java.util.Collections; + +public class EnumKeyMessage { + private final IEnumKeyCode subject; + /** + * Параметры для подстановки в текст сообщения. Все Serializable. + * args not null. + */ + private Object[] args; + + public EnumKeyMessage(IEnumKeyCode subject) { + this.subject = subject; + this.args = Collections.emptyList().toArray(new Object[0]); + } + + + public EnumKeyMessage(IEnumKeyCode subject, Object... args) { + this.subject = subject; + this.args = args; + } + + public IEnumKeyCode getSubject() { + return subject; + } + + public Object[] getArgs() { + return args; + } + + public void setArgs(Object[] args) { + this.args = args; + } + + @Override + public String toString() { + return "EnumKeyMessage{subject=" + subject + ", args: " + Arrays.toString(args) + "}"; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/enums/IEnumKeyCode.java b/src/main/java/ru/nbch/credit_tracker/enums/IEnumKeyCode.java new file mode 100644 index 0000000..ea808ce --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/enums/IEnumKeyCode.java @@ -0,0 +1,102 @@ +package ru.nbch.credit_tracker.enums; + +import java.io.Serializable; +import java.util.Objects; + +public interface IEnumKeyCode extends Serializable { + String getKey(); + String getCode(); + + default boolean equalsByKey(String key) { + return key != null && getKey().equalsIgnoreCase(key); + } + + /** + * Проверяет что среди данного набора Enum, присутствует элемент с данным id + * id может быть null + */ + static & IEnumKeyCode> boolean contains(String id, T... enumSet) { + for (T e : enumSet) { + if (Objects.equals(id, e.getKey())) + return true; + } + return false; + } + static & IEnumKeyCode> boolean containsIgnoreCase(String id, T... enumSet) { + for (T e : enumSet) { + if (e.getKey() != null && e.getKey().equalsIgnoreCase(id)) { + return true; + } + } + return false; + } + + static & IEnumKeyCode> boolean contains(T e, T... enumSet) { + for (T enumEl : enumSet) { + if (enumEl.equals(e)) + return true; + } + return false; + } + + /** + * Проверяет что среди всех Enum данного класса, присутствует элемент с данным id + * id может быть null + */ + static & IEnumKeyCode> boolean contains(String key, Class enumClass) { + for (T e : enumClass.getEnumConstants()) { + if (Objects.equals(key, e.getKey())) + return true; + } + return false; + } + + /** + * Возвращает Enum по id, если в заданном классе такой определен + * id может быть null + * + * @return Enum если нашел, иначе null + */ + static & IEnumKeyCode> T getEnumByKey(Class enumClass, String key) { + for (T e : enumClass.getEnumConstants()) { + if (Objects.equals(key, e.getKey())) + return e; + } + return null; + } + + static & IEnumKeyCode> T getEnumByKeyOrUndefined(Class enumClass, String key) { + T enumByKey = getEnumByKey(enumClass, key); + return enumByKey == null ? getUndefined(enumClass) : enumByKey; + } + + static & IEnumKeyCode> T getUndefined(Class enumClass) { + T enumByKey = getEnumByKey(enumClass, UNDEFINED_VALUE); + if (enumByKey == null) { + throw new IllegalArgumentException("Undefined value not found in enum " + enumClass); + } + return enumByKey; + } + + default boolean theSameAs(Object other) { + if (!(other instanceof IEnumKeyCode otherCasted)) { + return false; + } + return this.getKey().equalsIgnoreCase(otherCasted.getKey()); + } + + default String getErrorCode() { + String key = getKey(); + String[] split = key.split("\\."); + if (split.length < 1) return null; + String s = split[split.length - 1]; + if (!s.matches("\\d+")) return null; + return s; + } + + static String getKeyOrNull(IEnumKeyCode enumVal) { + return enumVal == null ? null : enumVal.getKey(); + } + + String UNDEFINED_VALUE = "_Undefined"; +} diff --git a/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java b/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java index 3a645e6..f3c8c1e 100644 --- a/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java +++ b/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java @@ -1,9 +1,6 @@ package ru.nbch.credit_tracker.enums; -import lombok.Getter; - -@Getter -public enum SignalError { +public enum SignalError implements IEnumKeyCode { ERROR_001("001", "Ошибка распаковки архива"), ERROR_002("002", "Отсутствует обязательный элемент: "), ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX"), @@ -31,4 +28,16 @@ public enum SignalError { this.code = code; this.message = message; } + + @Override + public String getKey() { + return message; + } + + @Override + public String getCode() { + return code; + } + + } diff --git a/src/main/java/ru/nbch/credit_tracker/enums/XmlErrorFormat.java b/src/main/java/ru/nbch/credit_tracker/enums/XmlErrorFormat.java new file mode 100644 index 0000000..45498ea --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/enums/XmlErrorFormat.java @@ -0,0 +1,10 @@ +package ru.nbch.credit_tracker.enums; + +public enum XmlErrorFormat { + MonitoringActivePackagesError, + MonitoringDownloadError, + MonitoringError, + MonitoringPackageDeleteError, + MonitoringReportListError + +} diff --git a/src/main/java/ru/nbch/credit_tracker/exceptions/SignalClientException.java b/src/main/java/ru/nbch/credit_tracker/exceptions/SignalClientException.java new file mode 100644 index 0000000..e6d85cb --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/exceptions/SignalClientException.java @@ -0,0 +1,18 @@ +package ru.nbch.credit_tracker.exceptions; + +import lombok.Getter; +import ru.nbch.credit_tracker.enums.EnumKeyMessage; +import ru.nbch.credit_tracker.enums.XmlErrorFormat; + +@Getter +public class SignalClientException extends RuntimeException { + + private final EnumKeyMessage errorMessage; + private final XmlErrorFormat errorFormat; + + public SignalClientException(EnumKeyMessage errorMessage, + XmlErrorFormat errorFormat) { + this.errorMessage = errorMessage; + this.errorFormat = errorFormat; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/exceptions/SignalErrorException.java b/src/main/java/ru/nbch/credit_tracker/exceptions/SignalErrorException.java index d3afa23..cad58f4 100644 --- a/src/main/java/ru/nbch/credit_tracker/exceptions/SignalErrorException.java +++ b/src/main/java/ru/nbch/credit_tracker/exceptions/SignalErrorException.java @@ -19,9 +19,9 @@ public class SignalErrorException extends RuntimeException { errorMessage.setStatus("error"); errorMessage.setErrorCode(error.getCode()); if (tag != null) { - errorMessage.setErrorText(error.getMessage() + " " + tag); + errorMessage.setErrorText(error.getKey() + " " + tag); } else { - errorMessage.setErrorText(error.getMessage()); + errorMessage.setErrorText(error.getKey()); } return errorMessage; } diff --git a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java index e1bff2d..d352328 100644 --- a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java +++ b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java @@ -1,5 +1,14 @@ package ru.nbch.credit_tracker.facade; +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.FileUrlResource; @@ -12,13 +21,19 @@ import ru.nbch.credit_tracker.entities.request.active_packages.MonitoringActiveP import ru.nbch.credit_tracker.entities.request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.request.monitoring_request.Subject; import ru.nbch.credit_tracker.entities.response.active_packages.MonitoringActivePackagesResponse; -import ru.nbch.credit_tracker.entities.response.errors.*; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringActivePackagesError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringDownloadError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringPackageDeleteError; +import ru.nbch.credit_tracker.entities.response.errors.MonitoringReportListError; import ru.nbch.credit_tracker.entities.response.success.MonitoringPackageDeleteResponse; import ru.nbch.credit_tracker.entities.response.success.MonitoringReportListResponse; import ru.nbch.credit_tracker.entities.response.success.MonitoringResponse; import ru.nbch.credit_tracker.entities.signal_package.Signals; import ru.nbch.credit_tracker.enums.SignalError; import ru.nbch.credit_tracker.enums.Status; +import ru.nbch.credit_tracker.enums.XmlErrorFormat; +import ru.nbch.credit_tracker.exceptions.SignalClientException; import ru.nbch.credit_tracker.exceptions.SignalErrorException; import ru.nbch.credit_tracker.service.SignalService; import ru.nbch.credit_tracker.service.indic.PhoneService; @@ -28,14 +43,8 @@ import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.jaxb.XmlUtilService; import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.ValidationUtil; - -import java.io.File; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; +import ru.nbch.credit_tracker.utils.ZipUtils; +import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.excp; @Slf4j @Service @@ -76,64 +85,44 @@ public class SignalFacade { } // Валидация Архива на количетсво файлов, размер и расширение - try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) { - int fileCount = 0; - ZipEntry xmlEntry = null; - - ZipEntry entry = zipInputStream.getNextEntry(); - while (entry != null) { - if (!entry.isDirectory()) { - fileCount++; - if (fileCount > 1) { // Архив должен содержать ровно один XML-файл - throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true); - } - - if (!entry.getName().toLowerCase().endsWith(".xml")) { // Файл должен быть с расширением xml - throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true); - } - - xmlEntry = entry; - } else { - throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true); // Архив должен содержать ровно один XML-файл. Никаких доп директорий - } - zipInputStream.closeEntry(); - entry = zipInputStream.getNextEntry(); - } - - if (fileCount == 0) { // Архив должен содержать ровно один XML-файл - throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true); - } - - if (xmlEntry.getSize() > MAX_XML_FILE_SIZE) { // максимально 2 Гб до запаковки - throw new SignalErrorException(SignalError.ERROR_009, null, new MonitoringError(), true); - } - - } catch (Exception e) { - if (e instanceof SignalErrorException) { - throw (SignalErrorException) e; - } - log.error(e.getMessage(), e); - throw new SignalErrorException(SignalError.ERROR_100, null, new MonitoringError(), false); - } - - // Теперь знаем, что в архиве лежит ровно один файл с раширением xml и он весит меньше 2гб. Можно валидировать сам xml - // Пробуем распарсить xml MonitoringRequest monitoringRequest; Integer packageId; - try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) { + ZipInputStream zipInputStream = null; + try { + zipInputStream = new ZipInputStream(file.getInputStream()); + Optional xmlFileEntry = ZipUtils.extractValidEntry(zipInputStream); + if (xmlFileEntry.isEmpty()) { + throw excp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError); + } + if (xmlFileEntry.get().getSize() > MAX_XML_FILE_SIZE) { + throw excp(SignalError.ERROR_009, XmlErrorFormat.MonitoringError); + } + + zipInputStream.close(); + zipInputStream = new ZipInputStream(file.getInputStream()); zipInputStream.getNextEntry(); IXmlProcessor processor = xmlUtilService.createJaxbProcessor(); monitoringRequest = processor.read(zipInputStream, MonitoringRequest.class); - } catch (Exception e) { - if (e instanceof SignalErrorException) { - throw (SignalErrorException) e; + if (e instanceof SignalClientException) { + throw (SignalClientException) e; } log.error(e.getMessage(), e); - throw new SignalErrorException(SignalError.ERROR_100, null, new MonitoringError(), false); + //todo throw new SignalServerException(e); + throw excp(SignalError.ERROR_100, XmlErrorFormat.MonitoringError); + } finally { + if (zipInputStream != null) { + try { + zipInputStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + //todo throw new SignalServerException(e); + } + } } + // валидация xml try { validateMonitoringRequest(monitoringRequest); @@ -247,8 +236,8 @@ public class SignalFacade { private boolean isZipFile(MultipartFile file) { return file.getContentType() != null && - (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) || - file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip"); + (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) || + file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip"); } public ResponseEntity findActivePackages(MonitoringActivePackagesRequest activePackagesRequest) { diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/jaxb/XmlUtilService.java b/src/main/java/ru/nbch/credit_tracker/service/xml/jaxb/XmlUtilService.java index d8a1081..77253e7 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/xml/jaxb/XmlUtilService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/jaxb/XmlUtilService.java @@ -35,6 +35,7 @@ import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; @Service public class XmlUtilService { + //сделать бином public IXmlProcessor createJaxbProcessor() throws JAXBException { return new XmlProcessorImplAnyClasses(Charset.forName("windows-1251"), // unmarshalling classes diff --git a/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java b/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java new file mode 100644 index 0000000..4d1438e --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java @@ -0,0 +1,28 @@ +package ru.nbch.credit_tracker.utils; + +import java.io.IOException; +import java.util.Optional; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ZipUtils { + public static Optional extractValidEntry(ZipInputStream inputStream) throws IOException { + Optional zipEntry = Optional.empty(); + ZipEntry entry; + while ((entry = inputStream.getNextEntry()) != null) { + if (!entry.isDirectory()) { + if (zipEntry.isPresent()) { + //если мы уже нашли валидный xml файл, то обработка всего файла недоступна, возвращаем пустой entry + return Optional.empty(); + } + if (entry.getName().toLowerCase().endsWith(".xml")) { // Файл должен быть с расширением xml + zipEntry = Optional.of(entry); + } + } + inputStream.closeEntry(); + } + return zipEntry; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/utils/error/ExceptionUtils.java b/src/main/java/ru/nbch/credit_tracker/utils/error/ExceptionUtils.java new file mode 100644 index 0000000..320aa87 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/utils/error/ExceptionUtils.java @@ -0,0 +1,15 @@ +package ru.nbch.credit_tracker.utils.error; + +import ru.nbch.credit_tracker.enums.EnumKeyMessage; +import ru.nbch.credit_tracker.enums.IEnumKeyCode; +import ru.nbch.credit_tracker.enums.XmlErrorFormat; +import ru.nbch.credit_tracker.exceptions.SignalClientException; + +public class ExceptionUtils { +// public static SignalClientException excp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat) { +// return new SignalClientException(new EnumKeyMessage(enumKeyCode), xmlErrorFormat); +// } + public static SignalClientException excp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Object... args) { + return new SignalClientException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat); + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/utils/error/SimpleMessageResolver.java b/src/main/java/ru/nbch/credit_tracker/utils/error/SimpleMessageResolver.java new file mode 100644 index 0000000..c557a8a --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/utils/error/SimpleMessageResolver.java @@ -0,0 +1,12 @@ +package ru.nbch.credit_tracker.utils.error; + +import java.util.Arrays; +import ru.nbch.credit_tracker.enums.EnumKeyMessage; + + +public class SimpleMessageResolver { + public String resolve(EnumKeyMessage errorMessage) { + if (errorMessage == null) return "null"; + return String.format("(%s) args %s", errorMessage.getSubject().getKey(), Arrays.toString(errorMessage.getArgs())); + } +}