diff --git a/config-data-executor/src/main/java/org/micord/controller/ApiController.java b/config-data-executor/src/main/java/org/micord/controller/ApiController.java index 5fe05f2..618c588 100644 --- a/config-data-executor/src/main/java/org/micord/controller/ApiController.java +++ b/config-data-executor/src/main/java/org/micord/controller/ApiController.java @@ -1,18 +1,14 @@ package org.micord.controller; import org.micord.enums.ConfigType; -import org.micord.exceptions.ValidationException; -import org.micord.models.requests.DownloadCSVRequest; import org.micord.models.requests.RequestParameters; import org.micord.service.ApiService; -import org.micord.service.ValidationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -23,7 +19,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.List; -import java.util.Map; /** * REST Controller for API operations. @@ -37,16 +32,9 @@ public class ApiController { @Autowired private ApiService apiService; - @Autowired - private ValidationService validationService; - @PostMapping("/removeMilitaryDraftNotices") public ResponseEntity removeMilitaryDraftNotices(@RequestBody RequestParameters request) throws SQLException, FileNotFoundException { - - validateRequestDates(request); - List ids = request.getIds(); - validationService.validateAll(ids); logger.debug("Starting removeMilitaryDraftNotices process for ids: {}", ids); apiService.process(ConfigType.REMOVE_MILITARY_DRAFT_NOTICES, request); @@ -56,7 +44,7 @@ public class ApiController { } @PostMapping("/deleteFiles") - public ResponseEntity deleteFiles(@RequestBody List ids) throws FileNotFoundException { + public ResponseEntity deleteFiles(@RequestBody List ids) throws FileNotFoundException, SQLException { apiService.process(ConfigType.DELETE_FILES, ids); @@ -65,7 +53,6 @@ public class ApiController { @PostMapping("/block") public ResponseEntity block(@RequestBody List ids) throws SQLException, FileNotFoundException { - validationService.validateAll(ids); logger.debug("Starting block process for ids: {}", ids); apiService.process(ConfigType.BLOCK, ids); @@ -77,7 +64,6 @@ public class ApiController { @PostMapping("/unblock") public ResponseEntity unblock(@RequestBody List ids) throws SQLException, FileNotFoundException { - validationService.validateAll(ids); logger.debug("Starting unblock process for ids: {}", ids); apiService.process(ConfigType.UNBLOCK, ids); @@ -89,7 +75,6 @@ public class ApiController { @PostMapping("/removeFromSystem") public ResponseEntity removeFromSystem(@RequestBody List ids) throws SQLException, FileNotFoundException { - validationService.validateAll(ids); logger.debug("Starting removeFromSystem process for ids: {}", ids); apiService.process(ConfigType.REMOVE_FROM_SYSTEM, ids); @@ -101,7 +86,6 @@ public class ApiController { @PostMapping("/removeFromCallList") public ResponseEntity removeFromCallList(@RequestBody List ids) throws SQLException, FileNotFoundException { - validationService.validateAll(ids); logger.debug("Starting removeFromCallList process for ids: {}", ids); apiService.process(ConfigType.REMOVE_FROM_CALL_LIST, ids); @@ -112,11 +96,9 @@ public class ApiController { @PostMapping("/downloadCSV") - public ResponseEntity downloadCSV(@RequestBody DownloadCSVRequest request) throws IOException { + public ResponseEntity downloadCSV(@RequestBody RequestParameters request) throws IOException { logger.debug("Starting downloadCSV process for request: {}", request.getType()); - validateRequestDates(request); - File csvFile = apiService.download(ConfigType.DOWNLOAD_CSV, request); InputStreamResource resource = new InputStreamResource(new FileInputStream(csvFile)); @@ -124,27 +106,12 @@ public class ApiController { return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + csvFile.getName()) - .contentType(MediaType.parseMediaType("text/csv")) + .contentType(MediaType.parseMediaType("text/csv; charset=UTF-8")) + .header(HttpHeaders.CONTENT_ENCODING, "UTF-8") .contentLength(csvFile.length()) .body(resource); } - private void validateRequestDates(DownloadCSVRequest request) { - if (request.getStartDate() != null && request.getEndDate() != null) { - if (request.getStartDate().isAfter(request.getEndDate())) { - throw new IllegalArgumentException("Start date must be before end date"); - } - } - } - - private void validateRequestDates(RequestParameters request) { - if (request.getStartDate() != null && request.getEndDate() != null) { - if (request.getStartDate().isAfter(request.getEndDate())) { - throw new IllegalArgumentException("Start date must be before end date"); - } - } - } - @GetMapping("/listDownloadTypes") public ResponseEntity> listDownloadTypes() throws FileNotFoundException { diff --git a/config-data-executor/src/main/java/org/micord/exceptions/GlobalExceptionHandler.java b/config-data-executor/src/main/java/org/micord/exceptions/GlobalExceptionHandler.java index 9f560d1..4204ae4 100644 --- a/config-data-executor/src/main/java/org/micord/exceptions/GlobalExceptionHandler.java +++ b/config-data-executor/src/main/java/org/micord/exceptions/GlobalExceptionHandler.java @@ -83,6 +83,14 @@ public class GlobalExceptionHandler { )); } + @ExceptionHandler(IllegalRequestParametersException.class) + public ResponseEntity handleIllegalRequestParametersException(IllegalRequestParametersException e) { + return ResponseEntity.badRequest().body(Map.of( + "message", "Произошла ошибка ввода данных. Проверьте правильность заполнения полей", + "details", e.getMessage() + )); + } + @ExceptionHandler(NoSuchElementException.class) public ResponseEntity handleNoSuchElementException(NoSuchElementException e) { logger.error("Resource not found: {}", e.getMessage()); diff --git a/config-data-executor/src/main/java/org/micord/exceptions/IllegalRequestParametersException.java b/config-data-executor/src/main/java/org/micord/exceptions/IllegalRequestParametersException.java new file mode 100644 index 0000000..08aa4a0 --- /dev/null +++ b/config-data-executor/src/main/java/org/micord/exceptions/IllegalRequestParametersException.java @@ -0,0 +1,13 @@ +package org.micord.exceptions; + +import lombok.Getter; + +@Getter +public class IllegalRequestParametersException extends IllegalArgumentException { + + + public IllegalRequestParametersException(String message) { + super(message); + } + +} diff --git a/config-data-executor/src/main/java/org/micord/models/Requests.java b/config-data-executor/src/main/java/org/micord/models/Requests.java deleted file mode 100644 index 61f9575..0000000 --- a/config-data-executor/src/main/java/org/micord/models/Requests.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.micord.models; - -import lombok.Setter; - -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlRootElement; -import java.util.List; - -/** - * @author Maksim Tereshin - */ -@Setter -@XmlRootElement(name = "Requests") -public class Requests { - - private List sqlRequests; - private List aqlRequests; - private List s3Requests; - private List downloadRequests; - - @XmlElement(name = "DownloadRequest") - public List getDownloadRequests() { - return downloadRequests; - } - - @XmlElement(name = "SqlRequest") - public List getSqlRequests() { - return sqlRequests; - } - - @XmlElement(name = "AqlRequest") - public List getAqlRequests() { - return aqlRequests; - } - - @XmlElement(name = "S3Request") - public List getS3Requests() { - return s3Requests; - } - -} diff --git a/config-data-executor/src/main/java/org/micord/models/requests/AqlRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/AqlRequest.java index 66df4fe..14089b6 100644 --- a/config-data-executor/src/main/java/org/micord/models/requests/AqlRequest.java +++ b/config-data-executor/src/main/java/org/micord/models/requests/AqlRequest.java @@ -1,26 +1,24 @@ package org.micord.models.requests; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElementWrapper; +import lombok.Getter; import lombok.Setter; import java.util.List; +@Getter @Setter +@XmlAccessorType(XmlAccessType.FIELD) public class AqlRequest extends BaseRequest { - private AqlConnectionParams aqlConnectionParams; - private List aqlRequestCollections; - @XmlElement(name = "AqlConnectionParams") - public AqlConnectionParams getAqlConnectionParams() { - return aqlConnectionParams; - } + private AqlConnectionParams aqlConnectionParams; @XmlElementWrapper(name = "AqlRequestCollections") @XmlElement(name = "AqlRequestCollection") - public List getAqlRequestCollections() { - return aqlRequestCollections; - } + private List aqlRequestCollections; } diff --git a/config-data-executor/src/main/java/org/micord/models/requests/AqlRequestCollection.java b/config-data-executor/src/main/java/org/micord/models/requests/AqlRequestCollection.java index bd62c5d..e6c5e39 100644 --- a/config-data-executor/src/main/java/org/micord/models/requests/AqlRequestCollection.java +++ b/config-data-executor/src/main/java/org/micord/models/requests/AqlRequestCollection.java @@ -8,6 +8,7 @@ import lombok.Setter; public class AqlRequestCollection { private String type; + private String dateAttribute; private String collectionUrl; @XmlAttribute(name = "type") @@ -15,6 +16,11 @@ public class AqlRequestCollection { return type; } + @XmlAttribute(name = "dateAttribute") + public String getDateAttribute() { + return dateAttribute; + } + @XmlValue public String getCollectionUrl() { return collectionUrl; diff --git a/config-data-executor/src/main/java/org/micord/models/requests/BaseRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/BaseRequest.java index dc4cd85..d11d271 100644 --- a/config-data-executor/src/main/java/org/micord/models/requests/BaseRequest.java +++ b/config-data-executor/src/main/java/org/micord/models/requests/BaseRequest.java @@ -1,29 +1,27 @@ package org.micord.models.requests; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlSeeAlso; +import lombok.Getter; import lombok.Setter; +import org.micord.models.requests.downloads.AQLDownloadRequest; +import org.micord.models.requests.downloads.SQLDownloadRequest; import java.util.List; -/** - * @author Maksim Tereshin - */ +@Getter @Setter -@XmlSeeAlso({SqlRequest.class, S3Request.class, AqlRequest.class}) +@XmlAccessorType(XmlAccessType.FIELD) public abstract class BaseRequest { - private List requestArguments; - private String requestURL; - @XmlElement(name = "RequestArgument") - public List getRequestArguments() { - return requestArguments; - } + private List requestArguments; @XmlElement(name = "RequestURL") - public String getRequestURL() { - return requestURL; - } + private String requestURL; + @XmlElement(name = "RequestValidationRules") + private RequestValidationRules requestValidationRules; } diff --git a/config-data-executor/src/main/java/org/micord/models/requests/DownloadCSVRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/DownloadCSVRequest.java deleted file mode 100644 index 920bf18..0000000 --- a/config-data-executor/src/main/java/org/micord/models/requests/DownloadCSVRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.micord.models.requests; - - -import lombok.Data; - -import java.time.LocalDate; -import java.util.List; - -@Data -public class DownloadCSVRequest { - private String type; - private List ids; - private LocalDate startDate; - private LocalDate endDate; -} - diff --git a/config-data-executor/src/main/java/org/micord/models/requests/DownloadRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/DownloadRequest.java deleted file mode 100644 index ae001c3..0000000 --- a/config-data-executor/src/main/java/org/micord/models/requests/DownloadRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.micord.models.requests; - -import jakarta.xml.bind.annotation.XmlElement; -import lombok.Setter; - - -@Setter -public class DownloadRequest extends BaseRequest { - - private SqlConnectionParams sqlConnectionParams; - private String downloadRequestType; - - @XmlElement(name = "SqlConnectionParams") - public SqlConnectionParams getSqlConnectionParams() { - return sqlConnectionParams; - } - - @XmlElement(name = "DownloadRequestType") - public String getDownloadRequestType() { - return downloadRequestType; - } - -} diff --git a/config-data-executor/src/main/java/org/micord/models/requests/RequestValidationRules.java b/config-data-executor/src/main/java/org/micord/models/requests/RequestValidationRules.java new file mode 100644 index 0000000..b361870 --- /dev/null +++ b/config-data-executor/src/main/java/org/micord/models/requests/RequestValidationRules.java @@ -0,0 +1,24 @@ +package org.micord.models.requests; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlRootElement; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@XmlRootElement(name = "RequestValidationRules") +@XmlAccessorType(XmlAccessType.FIELD) +public class RequestValidationRules { + + @XmlAttribute(name = "isEmptyIdsAllowed") + private Boolean isEmptyIdsAllowed; + + @XmlAttribute(name = "isEmptyDatesAllowed") + private Boolean isEmptyDatesAllowed; + + @XmlAttribute(name = "isIdsFormatted") + private Boolean isIdsFormatted; +} diff --git a/config-data-executor/src/main/java/org/micord/models/requests/Requests.java b/config-data-executor/src/main/java/org/micord/models/requests/Requests.java index 4630972..15bb20a 100644 --- a/config-data-executor/src/main/java/org/micord/models/requests/Requests.java +++ b/config-data-executor/src/main/java/org/micord/models/requests/Requests.java @@ -1,41 +1,35 @@ package org.micord.models.requests; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import lombok.Getter; import lombok.Setter; +import org.micord.models.requests.downloads.AQLDownloadRequest; +import org.micord.models.requests.downloads.SQLDownloadRequest; import java.util.List; -/** - * @author Maksim Tereshin - */ +@Getter @Setter @XmlRootElement(name = "Requests") +@XmlAccessorType(XmlAccessType.FIELD) public class Requests { - private List sqlRequests; - private List aqlRequests; - private List s3Requests; - private List downloadRequests; - - @XmlElement(name = "DownloadRequest") - public List getDownloadRequests() { - return downloadRequests; - } - @XmlElement(name = "SqlRequest") - public List getSqlRequests() { - return sqlRequests; - } + private List sqlRequests; @XmlElement(name = "AqlRequest") - public List getAqlRequests() { - return aqlRequests; - } + private List aqlRequests; @XmlElement(name = "S3Request") - public List getS3Requests() { - return s3Requests; - } + private List s3Requests; + + @XmlElement(name = "AQLDownloadRequest") + private List aqlDownloadRequests; + + @XmlElement(name = "SQLDownloadRequest") + private List sqlDownloadRequests; } diff --git a/config-data-executor/src/main/java/org/micord/models/requests/S3Request.java b/config-data-executor/src/main/java/org/micord/models/requests/S3Request.java index a9057d7..5b7f42a 100644 --- a/config-data-executor/src/main/java/org/micord/models/requests/S3Request.java +++ b/config-data-executor/src/main/java/org/micord/models/requests/S3Request.java @@ -1,19 +1,17 @@ package org.micord.models.requests; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; +import lombok.Getter; import lombok.Setter; -/** - * @author Maksim Tereshin - */ +@Getter @Setter +@XmlAccessorType(XmlAccessType.FIELD) public class S3Request extends BaseRequest { + @XmlElement(name = "S3ConnectionParams") private S3ConnectionParams s3ConnectionParams; - @XmlElement(name = "S3ConnectionParams") - public S3ConnectionParams getS3ConnectionParams() { - return s3ConnectionParams; - } - } diff --git a/config-data-executor/src/main/java/org/micord/models/requests/SqlRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/SqlRequest.java index 0d67779..73ab654 100644 --- a/config-data-executor/src/main/java/org/micord/models/requests/SqlRequest.java +++ b/config-data-executor/src/main/java/org/micord/models/requests/SqlRequest.java @@ -1,19 +1,17 @@ package org.micord.models.requests; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; +import lombok.Getter; import lombok.Setter; -/** - * @author Maksim Tereshin - */ +@Getter @Setter +@XmlAccessorType(XmlAccessType.FIELD) public class SqlRequest extends BaseRequest { + @XmlElement(name = "SqlConnectionParams") private SqlConnectionParams sqlConnectionParams; - @XmlElement(name = "SqlConnectionParams") - public SqlConnectionParams getSqlConnectionParams() { - return sqlConnectionParams; - } - } diff --git a/config-data-executor/src/main/java/org/micord/models/requests/downloads/AQLDownloadRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/downloads/AQLDownloadRequest.java new file mode 100644 index 0000000..3e0015f --- /dev/null +++ b/config-data-executor/src/main/java/org/micord/models/requests/downloads/AQLDownloadRequest.java @@ -0,0 +1,27 @@ +package org.micord.models.requests.downloads; + +import jakarta.xml.bind.annotation.*; +import lombok.Getter; +import lombok.Setter; +import org.micord.models.requests.AqlConnectionParams; +import org.micord.models.requests.AqlRequestCollection; +import org.micord.models.requests.SqlConnectionParams; + +import java.util.List; + + +@Getter +@Setter +@XmlAccessorType(XmlAccessType.FIELD) +public class AQLDownloadRequest extends BaseDownloadRequest { + + @XmlElement(name = "AqlConnectionParams") + private AqlConnectionParams aqlConnectionParams; + + @XmlElement(name = "DownloadRequestEntitySelectorQuery") + private String downloadRequestEntitySelectorQuery; + + @XmlElementWrapper(name = "AqlRequestCollections") + @XmlElement(name = "AqlRequestCollection") + private List aqlRequestCollections; +} diff --git a/config-data-executor/src/main/java/org/micord/models/requests/downloads/BaseDownloadRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/downloads/BaseDownloadRequest.java new file mode 100644 index 0000000..6e75b76 --- /dev/null +++ b/config-data-executor/src/main/java/org/micord/models/requests/downloads/BaseDownloadRequest.java @@ -0,0 +1,18 @@ +package org.micord.models.requests.downloads; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlSeeAlso; +import lombok.Getter; +import lombok.Setter; +import org.micord.models.requests.BaseRequest; + +@Getter +@Setter +@XmlAccessorType(XmlAccessType.FIELD) +public abstract class BaseDownloadRequest extends BaseRequest { + + @XmlElement(name = "DownloadRequestType") + private String downloadRequestType; +} diff --git a/config-data-executor/src/main/java/org/micord/models/requests/downloads/SQLDownloadRequest.java b/config-data-executor/src/main/java/org/micord/models/requests/downloads/SQLDownloadRequest.java new file mode 100644 index 0000000..f6ff827 --- /dev/null +++ b/config-data-executor/src/main/java/org/micord/models/requests/downloads/SQLDownloadRequest.java @@ -0,0 +1,19 @@ +package org.micord.models.requests.downloads; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.Getter; +import lombok.Setter; +import org.micord.models.requests.SqlConnectionParams; + + +@Setter +@Getter +@XmlAccessorType(XmlAccessType.FIELD) +public class SQLDownloadRequest extends BaseDownloadRequest { + + @XmlElement(name = "SqlConnectionParams") + private SqlConnectionParams sqlConnectionParams; + +} diff --git a/config-data-executor/src/main/java/org/micord/service/ApiService.java b/config-data-executor/src/main/java/org/micord/service/ApiService.java index e5b29a4..9c0a393 100644 --- a/config-data-executor/src/main/java/org/micord/service/ApiService.java +++ b/config-data-executor/src/main/java/org/micord/service/ApiService.java @@ -1,8 +1,7 @@ package org.micord.service; import org.micord.enums.ConfigType; -import org.micord.models.requests.DownloadCSVRequest; -import org.micord.models.requests.DownloadRequest; +import org.micord.models.requests.downloads.*; import org.micord.models.requests.RequestParameters; import org.micord.models.requests.Requests; import org.slf4j.Logger; @@ -13,9 +12,12 @@ import org.springframework.stereotype.Service; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.sql.SQLException; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service public class ApiService { @@ -31,37 +33,51 @@ public class ApiService { @Autowired private ConfigService configService; - public void process(ConfigType methodName, List ids) throws FileNotFoundException { + @Autowired + private ValidationService validationService; + + public void process(ConfigType methodName, List ids) throws FileNotFoundException, SQLException { Requests config = configService.getConfig(methodName, Requests.class); sqlAndAqlService.processSqlAndAqlRequests(config, ids); } - public void process(ConfigType methodName, RequestParameters parameters) throws FileNotFoundException { + public void process(ConfigType methodName, RequestParameters parameters) throws FileNotFoundException, SQLException { Requests config = configService.getConfig(methodName, Requests.class); sqlAndAqlService.processSqlAndAqlRequests(config, parameters); } - public File download(ConfigType methodName, DownloadCSVRequest request) throws IOException { + public File download(ConfigType methodName, RequestParameters downloadRequest) throws IOException { Requests config = configService.getConfig(methodName, Requests.class); - String type = request.getType(); - List ids = Optional.ofNullable(request.getIds()) + String type = downloadRequest.getType(); + List ids = Optional.ofNullable(downloadRequest.getIds()) .filter(list -> !list.isEmpty()) .orElse(null); - DownloadRequest selectedRequest = config.getDownloadRequests().stream() + BaseDownloadRequest selectedRequest = config.getAqlDownloadRequests().stream() .filter(r -> r.getDownloadRequestType().equals(type)) .findFirst() + .map(BaseDownloadRequest.class::cast) + .or(() -> config.getSqlDownloadRequests().stream() + .filter(r -> r.getDownloadRequestType().equals(type)) + .findFirst() + .map(BaseDownloadRequest.class::cast)) .orElseThrow(() -> new IllegalArgumentException("Invalid download type: " + type)); - return downloadService.download(selectedRequest, ids, request.getStartDate(), request.getEndDate()); + Map validationResults = validationService.validateDownloadRequest(selectedRequest, downloadRequest, ids); + + return downloadService.download(selectedRequest, ids, downloadRequest, validationResults); } public List getDownloadTypes(ConfigType methodName) throws FileNotFoundException { Requests config = configService.getConfig(methodName, Requests.class); - return config.getDownloadRequests().stream() - .map(DownloadRequest::getDownloadRequestType) + return Stream.concat( + config.getSqlDownloadRequests().stream(), + config.getAqlDownloadRequests().stream() + ) + .map(BaseDownloadRequest::getDownloadRequestType) + .distinct() .collect(Collectors.toList()); } diff --git a/config-data-executor/src/main/java/org/micord/service/DownloadService.java b/config-data-executor/src/main/java/org/micord/service/DownloadService.java index de27af8..7fcb4fa 100644 --- a/config-data-executor/src/main/java/org/micord/service/DownloadService.java +++ b/config-data-executor/src/main/java/org/micord/service/DownloadService.java @@ -1,7 +1,15 @@ package org.micord.service; +import com.arangodb.ArangoCursor; +import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; +import com.arangodb.model.AqlQueryOptions; +import org.micord.config.ArangoDBConnection; import org.micord.config.DatabaseConnection; -import org.micord.models.requests.DownloadRequest; +import org.micord.models.requests.RequestParameters; +import org.micord.models.requests.downloads.AQLDownloadRequest; +import org.micord.models.requests.downloads.BaseDownloadRequest; +import org.micord.models.requests.downloads.SQLDownloadRequest; import org.micord.models.requests.RequestArgument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,10 +22,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @Service @@ -25,13 +30,59 @@ public class DownloadService { private static final Logger logger = LoggerFactory.getLogger(DownloadService.class); - public File download(DownloadRequest selectedRequest, List ids, LocalDate startDate, LocalDate endDate) { - - return processDownloadRequest(selectedRequest, ids, startDate, endDate); + public File download(BaseDownloadRequest selectedRequest, List ids, RequestParameters parameters, Map validationResults) { + LocalDate startDate = parameters.getStartDate(); + LocalDate endDate = parameters.getEndDate(); + if (selectedRequest instanceof SQLDownloadRequest) { + return processSqlDownloadRequest((SQLDownloadRequest) selectedRequest, ids, startDate, endDate, validationResults); + } else if (selectedRequest instanceof AQLDownloadRequest) { + return processAqlDownloadRequest((AQLDownloadRequest) selectedRequest, ids, startDate, endDate, validationResults); + } + throw new IllegalArgumentException("Unsupported request type: " + selectedRequest.getClass().getSimpleName()); } - private File processDownloadRequest(DownloadRequest request, List ids, LocalDate startDate, LocalDate endDate) { - Map query = buildSqlQuery(request, ids, startDate, endDate); + private File processAqlDownloadRequest(AQLDownloadRequest request, List ids, LocalDate startDate, LocalDate endDate, Map validationResults) { + ArangoDatabase arangoDb = ArangoDBConnection.getConnection(request.getAqlConnectionParams()); + + Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false); + Boolean emptyDatesAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_DATES_ALLOWED, false); + + + Map entities = executeSelectAqlRequest(arangoDb, request.getDownloadRequestEntitySelectorQuery(), ids, emptyIdsAllowed); + + if (entities.isEmpty()) { + logger.warn("No entities found for main AQL request."); + return null; + } + + List> results = new ArrayList<>(); + request.getAqlRequestCollections().forEach(collection -> { + String type = collection.getCollectionUrl(); + String entityType; + + if (Objects.equals(type, "applications")) { + entityType = "applicationId"; + } else { + entityType = type + "Id"; + } + + Object entityIds = entities.get(entityType); + + if (entityIds instanceof String) { + entityIds = Collections.singletonList((String) entityIds); + } + + String aqlQuery = buildAqlQuery(type, ids, collection.getDateAttribute(), startDate, endDate, emptyIdsAllowed, emptyDatesAllowed); + + results.addAll(executeAqlQuery(arangoDb, aqlQuery, (List) entityIds, startDate, endDate, emptyIdsAllowed, emptyDatesAllowed)); + }); + + return writeResultsToCsv(results); + } + + private File processSqlDownloadRequest(SQLDownloadRequest request, List ids, LocalDate startDate, LocalDate endDate, Map validationResults) { + + Map query = buildSqlQuery(request, ids, startDate, endDate, validationResults); try (Connection connection = DatabaseConnection.getConnection( request.getSqlConnectionParams())) { String requestURL = (String) query.get("requestURL"); @@ -62,6 +113,134 @@ public class DownloadService { return null; } + private Map executeSelectAqlRequest(ArangoDatabase arangoDb, + String downloadRequestEntitySelectorQuery, + List ids, Boolean emptyIdsAllowed) { + Map entities = new HashMap<>(); + + + Map bindVars = new HashMap<>(); + + if (!emptyIdsAllowed) bindVars.put("ids", ids); + + AqlQueryOptions aqlQueryOptions = new AqlQueryOptions(); + + try (ArangoCursor cursor = arangoDb.query(downloadRequestEntitySelectorQuery, Map.class, bindVars, aqlQueryOptions)) { + while (cursor.hasNext()) { + Map result = cursor.next(); + + for (Map.Entry entry : result.entrySet()) { + String key = entry.getKey(); + Object entityValue = entry.getValue(); + + entities.put(key, entityValue); + } + } + } + catch (Exception e) { + logger.error("Failed to execute AQL url", e); + } + + return entities; + } + + private String buildAqlQuery(String collectionName, List ids, String dateAttribute, LocalDate startDate, LocalDate endDate, Boolean emptyIdsAllowed, Boolean emptyDatesAllowed) { + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append("FOR doc IN ").append(collectionName).append(" "); + + List conditions = new ArrayList<>(); + if (!emptyIdsAllowed && ids != null && !ids.isEmpty()) { + conditions.add("doc._key IN @ids"); + } + if (!emptyDatesAllowed && dateAttribute != null) { + if (startDate != null) { + conditions.add("doc." + dateAttribute + " >= @startDate"); + } + if (endDate != null) { + conditions.add("doc." + dateAttribute + " <= @endDate"); + } + } + + if (!conditions.isEmpty()) { + queryBuilder.append("FILTER ").append(String.join(" AND ", conditions)).append(" "); + } + + queryBuilder.append("RETURN doc"); + + return queryBuilder.toString(); + } + + private List> executeAqlQuery(ArangoDatabase arangoDb, String aqlQuery, List ids, LocalDate startDate, LocalDate endDate, Boolean emptyIdsAllowed, Boolean emptyDatesAllowed) { + List> results = new ArrayList<>(); + + + try { + Map bindVars = new HashMap<>(); + if (!emptyIdsAllowed && ids != null && !ids.isEmpty()) { + bindVars.put("ids", ids); + } + if (!emptyDatesAllowed) { + if (startDate != null) { + bindVars.put("startDate", startDate.toString()); + } + if (endDate != null) { + bindVars.put("endDate", endDate.toString()); + } + } + + logger.info("Executing AQL query: {}\nWith bindVars: {}", aqlQuery, bindVars); + + AqlQueryOptions aqlQueryOptions = new AqlQueryOptions(); + try (ArangoCursor cursor = arangoDb.query(aqlQuery, Map.class, bindVars, aqlQueryOptions)) { + while (cursor.hasNext()) { + results.add(cursor.next()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + } catch (ArangoDBException e) { + logger.error("AQL query execution failed: {}\nError: {}", aqlQuery, e.getMessage(), e); + } + return results; + } + + private File writeResultsToCsv(List> results) { + File csvFile; + try { + csvFile = File.createTempFile("arango-download-", ".csv"); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), StandardCharsets.UTF_8))) { + + if (!results.isEmpty()) { + List headers = new ArrayList<>(results.get(0).keySet()); + writer.write(String.join(",", headers)); + writer.newLine(); + + for (Map row : results) { + List rowValues = headers.stream() + .map(header -> formatCsvField(row.get(header))) + .collect(Collectors.toList()); + writer.write(String.join(",", rowValues)); + writer.newLine(); + } + } + } + } catch (IOException e) { + logger.error("Failed to write results to CSV", e); + return null; + } + return csvFile; + } + + private String formatCsvField(Object value) { + if (value == null) { + return "\"\""; + } + String strValue = value.toString().replace("\"", "\"\""); + return "\"" + strValue + "\""; + } + private String formatCsvRow(String[] row) { StringBuilder formattedRow = new StringBuilder(); @@ -85,16 +264,27 @@ public class DownloadService { return field.replace("\"", "\"\""); } - private Map buildSqlQuery(DownloadRequest request, List ids, LocalDate startDate, LocalDate endDate) { + private Map buildSqlQuery(SQLDownloadRequest request, List ids, LocalDate startDate, LocalDate endDate, Map validationResults) { + Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false); + Boolean emptyDatesAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_DATES_ALLOWED, false); + + Map resultMap = new HashMap<>(); String endpointArguments; - String requestURL = prepareRequestURL(request, startDate, endDate); + String requestURL = request.getRequestURL(); + + if (!emptyDatesAllowed) { + requestURL = prepareRequestURL(request, startDate, endDate); + } + + if (emptyIdsAllowed != null && emptyIdsAllowed) { + resultMap.put("requestURL", requestURL.replace("where id in ${endpointArguments}", "")); + return resultMap; + } if (ids == null || ids.isEmpty()) { - resultMap.put("requestURL", requestURL - .replace("where id in ${endpointArguments}", "")); - + resultMap.put("requestURL", requestURL.replace("where id in ${endpointArguments}", "")); return resultMap; } @@ -143,8 +333,8 @@ public class DownloadService { return resultMap; } - private String prepareRequestURL(DownloadRequest request, LocalDate startDate, - LocalDate endDate) { + private String prepareRequestURL(SQLDownloadRequest request, LocalDate startDate, + LocalDate endDate) { String requestURL = request.getRequestURL(); if (startDate != null) { diff --git a/config-data-executor/src/main/java/org/micord/service/RequestService.java b/config-data-executor/src/main/java/org/micord/service/RequestService.java index 367abdb..0cca48f 100644 --- a/config-data-executor/src/main/java/org/micord/service/RequestService.java +++ b/config-data-executor/src/main/java/org/micord/service/RequestService.java @@ -10,6 +10,7 @@ import org.micord.config.ArangoDBConnection; import org.micord.config.DatabaseConnection; import org.micord.config.S3HttpConnection; import org.micord.enums.RequestArgumentType; +import org.micord.exceptions.IllegalRequestParametersException; import org.micord.models.requests.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,6 +23,7 @@ import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import java.io.FileNotFoundException; import java.io.StringReader; import java.net.HttpURLConnection; import java.net.http.HttpClient; @@ -36,9 +38,6 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; -/** - * @author Maksim Tereshin - */ @Service public class RequestService { @@ -47,23 +46,10 @@ public class RequestService { @Autowired private HttpClient httpClient; - public void processS3Requests(List s3Requests, List ids) { - logger.info("A. Starting processing of S3 requests"); + @Autowired + private ValidationService validationService; - for (S3Request request : s3Requests) { - processS3Request(request, ids); - } - } - - public void processS3Requests(List s3Requests, RequestParameters parameters) { - logger.info("A. Starting processing of S3 requests"); - - for (S3Request request : s3Requests) { - processS3Request(request, parameters); - } - } - - private void processS3Request(S3Request request, RequestParameters parameters) { + private void processS3Request(S3Request request, RequestParameters parameters, Map validationResults) { logger.info("B. Starting processing of single S3 request"); try { List files = new ArrayList<>(); @@ -74,7 +60,7 @@ public class RequestService { try (Connection connection = DatabaseConnection.getConnection( argument.getRequestArgumentConnectionParams())) { - Map query = buildSqlQueryForS3(argument.getRequestArgumentURL(), parameters); + Map query = buildSqlQueryForS3(argument.getRequestArgumentURL(), parameters, validationResults); logger.info("C. Calling query {} for ids {}: ", query.get("requestURL"), ids); logger.debug("Starting fetching paths from database for S3 request"); long startExecTime = System.currentTimeMillis(); @@ -126,8 +112,9 @@ public class RequestService { } } - private void processS3Request(S3Request request, List ids) { + private void processS3Request(S3Request request, List ids, Map validationResults) { logger.info("B. Starting processing of single S3 request"); + Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false); try { List files = new ArrayList<>(); @@ -136,17 +123,21 @@ public class RequestService { try (Connection connection = DatabaseConnection.getConnection( argument.getRequestArgumentConnectionParams())) { - Map query = buildSqlQueryForS3(argument.getRequestArgumentURL(), ids); - logger.info("C. Calling query {} for ids {}: ", query.get("requestURL"), ids); + String requestURL = argument.getRequestArgumentURL(); + if (!emptyIdsAllowed) { + Map sqlQueryForS3 = buildSqlQueryForS3(argument.getRequestArgumentURL(), ids); + requestURL = (String) sqlQueryForS3.get("requestURL"); + } + logger.info("C. Calling query {} for ids {}: ", requestURL, ids); logger.debug("Starting fetching paths from database for S3 request"); long startExecTime = System.currentTimeMillis(); - List result = fetchFileListFromDatabaseSQL(connection, (String) query.get("requestURL")); + List result = fetchFileListFromDatabaseSQL(connection, requestURL); String formattedResult = IntStream.range(0, result.size()) .mapToObj(i -> (i + 1) + ". " + result.get(i)) .collect(Collectors.joining("\n")); - logger.info("D. Found files for query {}:\n{}", query.get("requestURL"), formattedResult); + logger.info("D. Found files for query {}:\n{}", requestURL, formattedResult); if (result != null && !result.isEmpty()) { files.addAll(result); @@ -231,75 +222,99 @@ public class RequestService { @Transactional - public void processSqlAndAqlRequests(Requests config, List ids) { + public void processSqlAndAqlRequests(Requests config, List ids) throws SQLException, FileNotFoundException { logger.debug("Starting transactional processing of requests"); if (config.getS3Requests() != null && !config.getS3Requests().isEmpty()) { - processS3Requests(config.getS3Requests(), ids); + logger.info("A. Starting processing of S3 requests"); + + for (S3Request request : config.getS3Requests()) { + Map validationResults = validationService.validateRequest(request, ids); + processS3Request(request, ids, validationResults); + } } if (config.getSqlRequests() != null) { for (SqlRequest request : config.getSqlRequests()) { - processSqlRequests(request, ids); + Map validationResults = validationService.validateRequest(request, ids); + processSqlRequests(request, ids, validationResults); } } if (config.getAqlRequests() != null) { for (AqlRequest request : config.getAqlRequests()) { - processAqlRequests(request, ids); + Map validationResults = validationService.validateRequest(request, ids); + processAqlRequests(request, ids, validationResults); } } } @Transactional - public void processSqlAndAqlRequests(Requests config, RequestParameters parameters) { + public void processSqlAndAqlRequests(Requests config, RequestParameters parameters) throws SQLException, FileNotFoundException { logger.debug("Starting transactional processing of requests"); - if (config.getS3Requests() != null && !config.getS3Requests().isEmpty()) { - processS3Requests(config.getS3Requests(), parameters); + if (config.getS3Requests() != null && !config.getS3Requests().isEmpty()) { + + logger.info("A. Starting processing of S3 requests"); + + for (S3Request request : config.getS3Requests()) { + Map validationResults = validationService.validateMilitaryNoticeRequest(request, parameters); + processS3Request(request, parameters, validationResults); + } } if (config.getSqlRequests() != null) { for (SqlRequest request : config.getSqlRequests()) { - processSqlRequests(request, parameters); + Map validationResults = validationService.validateMilitaryNoticeRequest(request, parameters); + processSqlRequests(request, parameters, validationResults); } } if (config.getAqlRequests() != null) { for (AqlRequest request : config.getAqlRequests()) { - processAqlRequests(request, parameters.getIds()); + Map validationResults = validationService.validateMilitaryNoticeRequest(request, parameters); + processAqlRequests(request, parameters.getIds(), validationResults); } } } - private void processSqlRequests(SqlRequest request, List ids) { + private void processSqlRequests(SqlRequest request, List ids, Map validationResults) { logger.debug("Starting transactional processing of SQL requests"); - Map query = buildSqlQuery(request, ids); + Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false); + String requestURL; + Map query = null; + + if (emptyIdsAllowed != null && emptyIdsAllowed) { + requestURL = request.getRequestURL(); + logger.info("Empty IDs allowed. Using original request URL: {}", requestURL); + } else { + query = buildSqlQuery(request, ids); + requestURL = (String) query.get("requestURL"); + } + logger.debug("Opening connection for SQL Request: {}", request.getRequestURL()); long startExecTime = System.currentTimeMillis(); - try (Connection connection = DatabaseConnection.getConnection( - request.getSqlConnectionParams())) { - String requestURL = (String) query.get("requestURL"); + try (Connection connection = DatabaseConnection.getConnection(request.getSqlConnectionParams())) { executeSqlQuery(connection, requestURL); - List queryIds = (List) query.get("ids"); - if (queryIds != null && !queryIds.isEmpty()) { - ids.addAll(queryIds); - } else { - logger.warn("No IDs found for the query"); + if (query != null) { + List queryIds = (List) query.get("ids"); + if (queryIds != null && !queryIds.isEmpty()) { + ids.addAll(queryIds); + } else { + logger.warn("No IDs found for the query"); + } } long endExecTime = System.currentTimeMillis(); logger.debug("SQL request executed in {} ms", endExecTime - startExecTime); - logger.info("Successfully executed query {} for IDs: ({})", requestURL, String.join(", ", ids)); - } - catch (SQLException e) { - logger.error("SQL execution failed for query: {}", query, e); + } catch (SQLException e) { + logger.error("SQL execution failed for query: {}", requestURL, e); throw new RuntimeException("Error executing SQL query", e); } } - private void processSqlRequests(SqlRequest request, RequestParameters parameters) { + private void processSqlRequests(SqlRequest request, RequestParameters parameters, Map validationResults) { logger.debug("Starting transactional processing of SQL requests"); - Map query = buildSqlQuery(request, parameters); + Map query = buildSqlQuery(request, parameters, validationResults); List ids = parameters.getIds(); logger.debug("Opening connection for SQL Request: {}", request.getRequestURL()); long startExecTime = System.currentTimeMillis(); @@ -350,33 +365,49 @@ public class RequestService { return resultMap; } - private Map buildSqlQueryForS3(String url, RequestParameters parameters) { + private Map buildSqlQueryForS3(String url, RequestParameters parameters, Map validationResults) { logger.debug("Starting building SQL query for request: {}", url); long startExecTime = System.currentTimeMillis(); Map resultMap = new HashMap<>(); - String endpointArguments; - String requestURL = prepareDatesFilterInRequestURL(url, parameters.getStartDate(), parameters.getEndDate()); + Boolean isEmptyDatesAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_DATES_ALLOWED, false); - List ids = parameters.getIds(); - - if (requestURL.contains(":=")) { - endpointArguments = "'{" + ids.stream() - .map(String::trim) - .collect(Collectors.joining(", ")) + "}'"; - } else { - endpointArguments = "(" + ids.stream() - .map(s -> "'" + s.trim() + "'") - .collect(Collectors.joining(", ")) + ")"; + String requestURL = applyDateFilter(url, parameters.getStartDate(), parameters.getEndDate(), isEmptyDatesAllowed); + if (isEmptyDatesAllowed) { + logger.info("Skipping date filtering as empty dates are allowed."); } - resultMap.put("requestURL", requestURL - .replace("${endpointArguments}", endpointArguments)); + String finalUrl = applyEndpointArguments(requestURL, parameters.getIds()); + resultMap.put("requestURL", finalUrl); long endExecTime = System.currentTimeMillis(); logger.debug("SQL query for S3 built in {} ms", endExecTime - startExecTime); - return resultMap; + return Collections.unmodifiableMap(resultMap); + } + + private String applyDateFilter(String url, LocalDate startDate, LocalDate endDate, boolean skipFiltering) { + if (!skipFiltering) { + return prepareDatesFilterInRequestURL(url, startDate, endDate); + } + return url; + } + + private String applyEndpointArguments(String requestURL, List ids) { + String endpointArguments = formatEndpointArguments(requestURL, ids); + return requestURL.replace("${endpointArguments}", endpointArguments); + } + + private String formatEndpointArguments(String requestURL, List ids) { + if (requestURL.contains(":=")) { + return "'{" + ids.stream() + .map(String::trim) + .collect(Collectors.joining(", ")) + "}'"; + } else { + return "(" + ids.stream() + .map(s -> "'" + s.trim() + "'") + .collect(Collectors.joining(", ")) + ")"; + } } private String prepareDatesFilterInRequestURL(String requestURL, LocalDate startDate, LocalDate endDate) { @@ -449,57 +480,43 @@ public class RequestService { return resultMap; } - private Map buildSqlQuery(BaseRequest request, RequestParameters parameters) { + private Map buildSqlQuery(BaseRequest request, RequestParameters parameters, Map validationResults) { logger.debug("Starting building SQL query for request: {}", request.getRequestURL()); long startExecTime = System.currentTimeMillis(); Map resultMap = new HashMap<>(); - String endpointArguments; - String requestURL = prepareDatesFilterInRequestURL(request.getRequestURL(), parameters.getStartDate(), parameters.getEndDate()); - List ids = parameters.getIds(); + Boolean isEmptyDatesAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_DATES_ALLOWED, false); - if (requestURL.contains(":=")) { - endpointArguments = "'{" + ids.stream() - .map(String::trim) - .collect(Collectors.joining(", ")) + "}'"; - } else { - endpointArguments = "(" + ids.stream() - .map(s -> "'" + s.trim() + "'") - .collect(Collectors.joining(", ")) + ")"; + String requestURL = applyDateFilter(request.getRequestURL(), parameters.getStartDate(), parameters.getEndDate(), isEmptyDatesAllowed); + if (isEmptyDatesAllowed) { + logger.info("Skipping date filtering as empty dates are allowed."); } + List ids = parameters.getIds(); if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) { for (RequestArgument argument : request.getRequestArguments()) { - if (argument.getRequestArgumentConnectionParams() != null) { logger.debug("Opening connection for SQL RequestArgument: {}", argument.getRequestArgumentName()); - try (Connection connection = DatabaseConnection.getConnection( - argument.getRequestArgumentConnectionParams())) { + try (Connection connection = DatabaseConnection.getConnection(argument.getRequestArgumentConnectionParams())) { String query = argument.getRequestArgumentURL(); List result = fetchFileListFromDatabaseSQL(connection, query); - resultMap.put("ids", result); - if (result != null && !result.isEmpty()) { String resultSet = "(" + result.stream() .map(s -> "'" + s.trim() + "'") .collect(Collectors.joining(", ")) + ")"; - requestURL = requestURL.replace("${" + argument.getRequestArgumentName() + "}", resultSet); - } - - } - catch (SQLException e) { + } catch (SQLException e) { logger.error("Failed to execute query for RequestArgument", e); } } } } - resultMap.put("requestURL", requestURL - .replace("${endpointArguments}", endpointArguments)); + String finalUrl = applyEndpointArguments(requestURL, ids); + resultMap.put("requestURL", finalUrl); long endExecTime = System.currentTimeMillis(); logger.debug("SQL query built in {} ms", endExecTime - startExecTime); @@ -526,7 +543,7 @@ public class RequestService { return results; } - private void processAqlRequests(AqlRequest request, List ids) { + private void processAqlRequests(AqlRequest request, List ids, Map validationResults) { ArangoDatabase arangoDb = ArangoDBConnection.getConnection(request.getAqlConnectionParams()); // TODO: implement for multiple request arguments @@ -548,7 +565,7 @@ public class RequestService { logger.info("Stream transaction started with ID: {}", transactionId); - Map entities = executeSelectAqlRequest(arangoDb, requestArgument, ids, transactionId); + Map entities = executeSelectAqlRequest(arangoDb, requestArgument, ids, transactionId, validationResults); if (entities.isEmpty()) { logger.warn("No entities found for main AQL request."); @@ -591,15 +608,18 @@ public class RequestService { private Map executeSelectAqlRequest(ArangoDatabase arangoDb, RequestArgument requestArgument, - List ids, String transactionId) { + List ids, String transactionId, Map validationResults) { Map entities = new HashMap<>(); String url = requestArgument.getRequestArgumentURL(); RequestArgumentType type = requestArgument.getType(); if (type == RequestArgumentType.AQL) { + Boolean emptyIdsAllowed = validationResults.getOrDefault(ValidationService.IS_EMPTY_IDS_ALLOWED, false); + Map bindVars = new HashMap<>(); - bindVars.put("ids", ids); + + if (!emptyIdsAllowed) bindVars.put("ids", ids); AqlQueryOptions aqlQueryOptions = new AqlQueryOptions().streamTransactionId(transactionId); @@ -619,20 +639,6 @@ public class RequestService { logger.error("Failed to execute AQL url", e); } } -// else if (type == RequestArgumentType.SQL) { -// if (requestArgument.getRequestArgumentConnectionParams() != null) { -// try (Connection connection = DatabaseConnection.getConnection( -// requestArgument.getRequestArgumentConnectionParams())) { -// String query = requestArgument.getRequestArgumentURL(); -// List result = fetchFileListFromDatabaseSQL(connection, query); -// -// entities.put(aqlCollectionWrite, result); -// } -// catch (SQLException e) { -// logger.error("Failed to execute query for RequestArgument", e); -// } -// } -// } return entities; } diff --git a/config-data-executor/src/main/java/org/micord/service/ValidationService.java b/config-data-executor/src/main/java/org/micord/service/ValidationService.java index 6eb675e..57020c0 100644 --- a/config-data-executor/src/main/java/org/micord/service/ValidationService.java +++ b/config-data-executor/src/main/java/org/micord/service/ValidationService.java @@ -2,7 +2,11 @@ package org.micord.service; import org.micord.config.DatabaseConnection; import org.micord.enums.ConfigType; +import org.micord.exceptions.IllegalRequestParametersException; import org.micord.exceptions.ValidationException; +import org.micord.models.requests.BaseRequest; +import org.micord.models.requests.RequestParameters; +import org.micord.models.requests.downloads.BaseDownloadRequest; import org.micord.models.validations.ValidationRule; import org.micord.models.validations.ValidationRules; import org.springframework.beans.factory.annotation.Autowired; @@ -13,18 +17,26 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDate; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service public class ValidationService { + public static final String IS_EMPTY_DATES_ALLOWED = "isEmptyDatesAllowed"; + public static final String IS_EMPTY_IDS_ALLOWED = "isEmptyIdsAllowed"; + @Autowired private ConfigService configService; + private boolean isRequestValidatedByValidationConfig = false; + private boolean isMilitaryNoticeRequestValidatedByValidationConfig = false; + public Map> validate(List ids, ValidationRule rule) throws SQLException { String query = rule.getRequestURL(); @@ -63,7 +75,95 @@ public class ValidationService { } } - public Map validateAll(List ids) throws ValidationException, FileNotFoundException, SQLException { + public Map validateRequest(BaseRequest request, List ids) throws ValidationException, SQLException, FileNotFoundException { + Map validationResults = validateIds(request, ids); + if (isRequestValidatedByValidationConfig) validateByValidationConfig(ids, BaseRequest.class); + + return validationResults; + } + + public Map validateMilitaryNoticeRequest(BaseRequest request, RequestParameters parameters) throws ValidationException, SQLException, FileNotFoundException { + List ids = parameters.getIds(); + validateIds(request, ids); + Map validation = validateDates(request, parameters); + + if (isMilitaryNoticeRequestValidatedByValidationConfig) validateByValidationConfig(ids, RequestParameters.class); + + return validation; + } + + public Map validateDownloadRequest(BaseDownloadRequest request, RequestParameters downloadRequest, List ids) throws ValidationException { + Map validateIds = validateIds(request, ids); + Map validateDates = validateDates(request, downloadRequest); + + Map merged = new HashMap<>(); + merged.putAll(validateIds); + merged.putAll(validateDates); + + return merged; + } + + private static Map validateIds(BaseRequest request, List ids) { + + if (request.getRequestValidationRules() == null) { + return Map.of(); + } + Boolean emptyIdsAllowed = request.getRequestValidationRules().getIsEmptyIdsAllowed(); + if (emptyIdsAllowed) { + return Map.of(ValidationService.IS_EMPTY_IDS_ALLOWED, emptyIdsAllowed); + } + + if (ids == null || ids.isEmpty()) { + throw new IllegalRequestParametersException("пустые идентификаторы не допускаются"); + } + + Boolean isIdsFormatted = request.getRequestValidationRules().getIsIdsFormatted(); + if (isIdsFormatted) { + String uuidRegex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"; + + boolean invalidIdFound = ids.stream().anyMatch(id -> !id.matches(uuidRegex)); + + if (invalidIdFound) { + throw new IllegalRequestParametersException("Некоторые идентификаторы не соответствуют формату GUID"); + } + } + return Map.of(ValidationService.IS_EMPTY_IDS_ALLOWED, emptyIdsAllowed); + } + + private static Boolean validateEmptyDates(BaseRequest request, T parameters) { + if (request.getRequestValidationRules() == null) { + return Boolean.FALSE; + } + Boolean emptyDatesAllowed = request.getRequestValidationRules().getIsEmptyDatesAllowed(); + LocalDate startDate = parameters.getStartDate(); + LocalDate endDate = parameters.getEndDate(); + if (Boolean.FALSE.equals(emptyDatesAllowed) && (startDate == null || endDate == null)) { + throw new IllegalRequestParametersException("пустые даты не допускаются"); + } + return emptyDatesAllowed; + } + + private Map validateDates(R request, T parameters) { + Boolean emptyDatesAllowed = validateEmptyDates(request, parameters); + if (!emptyDatesAllowed && parameters.getStartDate() != null && parameters.getEndDate() != null) { + if (parameters.getStartDate().isAfter(parameters.getEndDate())) { + throw new IllegalArgumentException("Start date must be before end date"); + } + } + + return Map.of(ValidationService.IS_EMPTY_DATES_ALLOWED, emptyDatesAllowed); + } + + public Map validateByValidationConfig(List ids, T c) throws ValidationException, FileNotFoundException, SQLException { + if (c instanceof BaseRequest) { + isRequestValidatedByValidationConfig = true; + } + + if (c instanceof RequestParameters) { + isMilitaryNoticeRequestValidatedByValidationConfig = true; + } + + ValidationRules config = configService.getConfig(ConfigType.VALIDATE_BLOCK, ValidationRules.class); if (config.getValidationRules() == null || config.getValidationRules().isEmpty()) { @@ -85,7 +185,7 @@ public class ValidationService { .toList(); if (!invalidColumns.isEmpty()) { - String message = "Запись UUID = " + id + " не удовлетворяет критериям валидации."; + String message = "Запись UUID = " + id + " имеет не отменённые временные меры или подписанные повестки."; invalidRecords.put(id, message); } }); @@ -97,5 +197,4 @@ public class ValidationService { return invalidRecords; } - } diff --git a/config/cde-xml/downloadCSV.xml b/config/cde-xml/downloadCSV.xml index ffd604a..eb4ea0d 100644 --- a/config/cde-xml/downloadCSV.xml +++ b/config/cde-xml/downloadCSV.xml @@ -1,5 +1,47 @@ - + + + Arango + + + + + applications + history + + + localhost + 8529 + root + test + _system + + + Type_A Select system_id_ern from public.recruits where id in ${endpointArguments}; @@ -15,8 +57,8 @@ person_registry 4000 - - + + Type_B Select system_id_ern from public.recruits where id in ${endpointArguments}; @@ -32,23 +74,6 @@ person_registry 4000 - - - Type_C - - Select system_id_ern from public.recruits where id in ${endpointArguments}; - - - 10.10.31.118 - 5432 - ervu - ervu - org.postgresql.Driver - org.postgresql.xa.PGXADataSource - 50 - person_registry - 4000 - - + \ No newline at end of file