From a2a1368518411e3a07c3b1381c568c835593b734 Mon Sep 17 00:00:00 2001 From: Julius Sula Date: Mon, 22 May 2023 15:45:46 +0200 Subject: [PATCH] add more errors and handle change --- .../calender/api/KalenderRestController.java | 5 +- .../com/nttdata/calender/changes/Change.java | 10 +- .../calender/changes/ChangeRequest.java | 149 ++++++++++-------- .../nttdata/calender/changes/query/Sort.java | 5 +- .../BackendError.java} | 12 +- .../ErrorTypes/NotFoundError.java | 9 ++ .../ErrorTypes/ValidationError.java | 11 ++ .../errorhandling/GlobalExceptionHandler.java | 18 ++- 8 files changed, 136 insertions(+), 83 deletions(-) rename backend/src/main/java/com/nttdata/calender/errorhandling/{NotFoundError.java => ErrorTypes/BackendError.java} (50%) create mode 100644 backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/NotFoundError.java create mode 100644 backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/ValidationError.java diff --git a/backend/src/main/java/com/nttdata/calender/api/KalenderRestController.java b/backend/src/main/java/com/nttdata/calender/api/KalenderRestController.java index 8ebc924..2f851e4 100644 --- a/backend/src/main/java/com/nttdata/calender/api/KalenderRestController.java +++ b/backend/src/main/java/com/nttdata/calender/api/KalenderRestController.java @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; - import com.bmc.arsys.api.ARException; import com.fasterxml.jackson.core.JsonProcessingException; import com.nttdata.calender.approval.Approval; @@ -23,6 +22,8 @@ import com.nttdata.calender.changes.ChangeResponse; import com.nttdata.calender.changes.ChangeUpdateRequest; import com.nttdata.calender.contracts.Contract; import com.nttdata.calender.contracts.ContractGetResponse; +import com.nttdata.calender.errorhandling.ErrorTypes.NotFoundError; +import com.nttdata.calender.errorhandling.ErrorTypes.ValidationError; import com.nttdata.calender.implementer.Implementer; import com.nttdata.calender.implementer.ImplementerGetRequest; import com.nttdata.calender.implementer.ImplementerGetResponse; @@ -176,7 +177,7 @@ public class KalenderRestController { @PostMapping("/api/getChanges") @ResponseBody public ChangeResponse getChanges(@RequestBody ChangeRequest request) - throws ARException { + throws ARException, NotFoundError, ValidationError { return change.get(request); } diff --git a/backend/src/main/java/com/nttdata/calender/changes/Change.java b/backend/src/main/java/com/nttdata/calender/changes/Change.java index 0775add..7b620d6 100644 --- a/backend/src/main/java/com/nttdata/calender/changes/Change.java +++ b/backend/src/main/java/com/nttdata/calender/changes/Change.java @@ -6,11 +6,11 @@ import java.util.ArrayList; import java.util.Date; import java.util.Optional; import java.util.TimeZone; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; - import com.bmc.arsys.api.ARException; import com.bmc.arsys.api.Entry; import com.bmc.arsys.api.Timestamp; @@ -18,7 +18,8 @@ import com.bmc.arsys.api.Value; import com.nttdata.calender.api.Query; import com.nttdata.calender.api.RemedyJavaAPI; import com.nttdata.calender.changes.query.Filter; -import com.nttdata.calender.errorhandling.NotFoundError; +import com.nttdata.calender.errorhandling.ErrorTypes.NotFoundError; +import com.nttdata.calender.errorhandling.ErrorTypes.ValidationError; /** * Class representing the change with all of the change specific attributes. @@ -39,7 +40,6 @@ public class Change { @Autowired public Change(RemedyJavaAPI api) { this.api = api; - // TODO: Contract this.queryChange = new Query.QueryBuilder(formName) .addFieldId("ChangeNr", 1000000182) .addFieldId("SupportGroup", 1000000015) @@ -74,7 +74,7 @@ public class Change { * @return a List of {@link Change} for every entry found * @throws ARException if an error occurs during the retrieval process */ - public ChangeResponse get(ChangeRequest request) throws ARException { + public ChangeResponse get(ChangeRequest request) throws ARException, NotFoundError, ValidationError { api.impersonateUser("ext_StanzPa"); var peopleFullName = processPeopleInfo(request); @@ -127,7 +127,7 @@ public class Change { return new ChangeResponse(entriesSize, changes); } - private String processPeopleInfo(ChangeRequest request) throws ARException { + private String processPeopleInfo(ChangeRequest request) throws ARException, NotFoundError { // Queries for SupportGroup of impersonated User var queryPerson = new Query.QueryBuilder("CTM:Support Group Association") .addFieldId("FullName", 1000000017) diff --git a/backend/src/main/java/com/nttdata/calender/changes/ChangeRequest.java b/backend/src/main/java/com/nttdata/calender/changes/ChangeRequest.java index 8d53f3b..422bcbc 100644 --- a/backend/src/main/java/com/nttdata/calender/changes/ChangeRequest.java +++ b/backend/src/main/java/com/nttdata/calender/changes/ChangeRequest.java @@ -2,6 +2,7 @@ package com.nttdata.calender.changes; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import com.bmc.arsys.api.ARException; @@ -9,6 +10,7 @@ import com.nttdata.calender.api.Query; import com.nttdata.calender.api.RemedyJavaAPI; import com.nttdata.calender.changes.query.Filter; import com.nttdata.calender.changes.query.Sort; +import com.nttdata.calender.errorhandling.ErrorTypes.ValidationError; /** * Represents a change request object that stores information about slice start, @@ -113,76 +115,97 @@ public class ChangeRequest { * invalid filter is provided. */ // TODO: Exception handling (unsuppoprted qualifier) - public String constructQualifier(Query query, RemedyJavaAPI api) throws ARException { + public String constructQualifier(Query query, RemedyJavaAPI api) throws ARException, ValidationError { var qualifier = ""; for (int i = 0; i < this.filter.size(); i++) { - var column = this.filter.get(i).getColumn(); - if (!column.isEmpty()) { - var inner_qualifier = ""; + var current_filter = this.filter.get(i); + var column = current_filter.getColumn(); + var criteria = current_filter.getCriteria(); - if (column.equals("D2")) { - var startFrom = filter.get(i).getCriteria()[0]; - var startTo = filter.get(i).getCriteria()[1]; - - String inputFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - String outputFormat = "dd/MM/yyyy"; - - LocalDateTime dateTimeFrom = LocalDateTime.parse(startFrom, DateTimeFormatter.ofPattern(inputFormat)); - LocalDateTime dateTimeTo = LocalDateTime.parse(startTo, DateTimeFormatter.ofPattern(inputFormat)); - String startFromFormatted = dateTimeFrom.format(DateTimeFormatter.ofPattern(outputFormat)); - String startToFormatted = dateTimeTo.format(DateTimeFormatter.ofPattern(outputFormat)); - - if (!startFromFormatted.isEmpty() && !startToFormatted.isEmpty()) { - var dateColumn = api.getFieldDatabaseName(query.getFormName(), query.getFieldId(column)); - - // Same day changes need to startFrom=day and startTo=day+24h 60m 60s - if (startFromFormatted.equals(startToFormatted)) { - startToFormatted = "\' < (\"" + startToFormatted + "\"" + " + (24 * (60 * 60)))"; - } else - startToFormatted = "\' <= \"" + startToFormatted + "\""; - - qualifier += "\'" + dateColumn + "\' >= \"" + startFromFormatted + "\" AND "; - qualifier += "\'" + dateColumn + startToFormatted; - } - qualifier = "(" + qualifier + ")"; - } else { - column = api.getFieldDatabaseName(query.getFormName(), query.getFieldId(column)); - - var inner_filter = "\'" + column + "\' "; - var criterias = filter.get(i).getCriteria(); - var inner_concat = " OR "; - var inner_criteria_prefix = ""; - - switch (filter.get(i).getFilter()) { - case "equals": - inner_filter += "= "; - break; - case "contains": - inner_filter += "LIKE "; - inner_concat = " AND "; - inner_criteria_prefix = "%"; - break; - default: - throw new ARException(); - } - - for (int j = 0; j < criterias.length; j++) { - criterias[j] = inner_criteria_prefix + criterias[j] + inner_criteria_prefix; - inner_qualifier += "(" + inner_filter + "\"" + criterias[j] + "\")"; - if (j < criterias.length - 1) { - inner_qualifier += inner_concat; - } - } - qualifier += "(" + inner_qualifier + ")"; - } - - if (i < filter.size() - 1) { - qualifier += " AND "; - } + if (column.isEmpty() || criteria.length <= 0) { + throw new ValidationError("Fields inside filter empty"); } + var inner_qualifier = ""; + + if (column.equals("D2")) { + var dateQualifier = constructDateQualifier(current_filter, column, query, api); + qualifier = "(" + dateQualifier + ")"; + } else { + column = api.getFieldDatabaseName(query.getFormName(), query.getFieldId(column)); + + var inner_filter = "\'" + column + "\' "; + var criterias = current_filter.getCriteria(); + var inner_concat = " OR "; + var inner_criteria_prefix = ""; + + switch (current_filter.getFilter()) { + case "equals": + inner_filter += "= "; + break; + case "contains": + inner_filter += "LIKE "; + inner_concat = " AND "; + inner_criteria_prefix = "%"; + break; + default: + throw new ARException(); + } + + for (int j = 0; j < criterias.length; j++) { + criterias[j] = inner_criteria_prefix + criterias[j] + inner_criteria_prefix; + inner_qualifier += "(" + inner_filter + "\"" + criterias[j] + "\")"; + if (j < criterias.length - 1) { + inner_qualifier += inner_concat; + } + } + qualifier += "(" + inner_qualifier + ")"; + } + + if (i < filter.size() - 1) { + qualifier += " AND "; + } + } return qualifier; } + + private String constructDateQualifier(Filter current_filter, String column, Query query, RemedyJavaAPI api) + throws ValidationError, ARException { + if (current_filter.getCriteria().length != 2) { + throw new ValidationError("Date Filter does not contain 2 date elements"); + } + var startFrom = current_filter.getCriteria()[0]; + var startTo = current_filter.getCriteria()[1]; + var startFromFormatted = convertDate(startFrom); + var startToFormatted = convertDate(startTo); + var qualifier = ""; + + var dateColumn = api.getFieldDatabaseName(query.getFormName(), query.getFieldId(column)); + + // Same day changes need to startFrom=day and startTo=day+24h 60m 60s + if (startFromFormatted.equals(startToFormatted)) { + startToFormatted = "\' < (\"" + startToFormatted + "\"" + " + (24 * (60 * 60)))"; + } else + startToFormatted = "\' <= \"" + startToFormatted + "\""; + + qualifier += "\'" + dateColumn + "\' >= \"" + startFromFormatted + "\" AND "; + qualifier += "\'" + dateColumn + startToFormatted; + return qualifier; + } + + private String convertDate(String date) throws ValidationError { + String inputFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + String outputFormat = "dd/MM/yyyy"; + + LocalDateTime parser = LocalDateTime.parse(date, DateTimeFormatter.ofPattern(inputFormat)); + var parsed = ""; + try { + parsed = parser.format(DateTimeFormatter.ofPattern(outputFormat)); + } catch (DateTimeParseException e) { + throw new ValidationError("Provided date format cannot be parsed into Remedy specific date format"); + } + return parsed; + } } diff --git a/backend/src/main/java/com/nttdata/calender/changes/query/Sort.java b/backend/src/main/java/com/nttdata/calender/changes/query/Sort.java index 3e19245..e974ef4 100644 --- a/backend/src/main/java/com/nttdata/calender/changes/query/Sort.java +++ b/backend/src/main/java/com/nttdata/calender/changes/query/Sort.java @@ -4,6 +4,7 @@ import com.bmc.arsys.api.Constants; import com.bmc.arsys.api.SortInfo; import com.nttdata.calender.api.Query; import com.nttdata.calender.changes.Change; +import com.nttdata.calender.errorhandling.ErrorTypes.ValidationError; /** * Defines the sort object needed for the retrieval of {@link Change}. @@ -58,7 +59,7 @@ public class Sort { * the column * @return the constructed SortInfo object */ - public SortInfo getSortInfo(Query changeQuery) { + public SortInfo getSortInfo(Query changeQuery) throws ValidationError { var column = changeQuery.getFieldId(this.column); // TODO: handle default of sortOrder int sortOrder = 0; @@ -70,6 +71,8 @@ public class Sort { case "dsc": sortOrder = Constants.AR_SORT_DESCENDING; break; + default: + throw new ValidationError("Unknown sort order specified"); } return new SortInfo(column, sortOrder); diff --git a/backend/src/main/java/com/nttdata/calender/errorhandling/NotFoundError.java b/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/BackendError.java similarity index 50% rename from backend/src/main/java/com/nttdata/calender/errorhandling/NotFoundError.java rename to backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/BackendError.java index 5ec8167..59837f8 100644 --- a/backend/src/main/java/com/nttdata/calender/errorhandling/NotFoundError.java +++ b/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/BackendError.java @@ -1,19 +1,17 @@ -package com.nttdata.calender.errorhandling; +package com.nttdata.calender.errorhandling.ErrorTypes; import org.springframework.http.HttpStatus; -public class NotFoundError extends RuntimeException { +public abstract class BackendError extends Exception { private int errorCode; private HttpStatus httpStatus; - public NotFoundError(String message) { + public BackendError(String message, int errorCode, HttpStatus httpStatus) { super(message); - this.errorCode = 404; - this.httpStatus = HttpStatus.NOT_FOUND; + this.errorCode = errorCode; + this.httpStatus = httpStatus; } - // Include getters for errorCode and httpStatus - public int getErrorCode() { return errorCode; } diff --git a/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/NotFoundError.java b/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/NotFoundError.java new file mode 100644 index 0000000..7f10c51 --- /dev/null +++ b/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/NotFoundError.java @@ -0,0 +1,9 @@ +package com.nttdata.calender.errorhandling.ErrorTypes; + +import org.springframework.http.HttpStatus; + +public class NotFoundError extends BackendError { + public NotFoundError(String message) { + super(message, 404, HttpStatus.NOT_FOUND); + } +} diff --git a/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/ValidationError.java b/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/ValidationError.java new file mode 100644 index 0000000..ee2dfbe --- /dev/null +++ b/backend/src/main/java/com/nttdata/calender/errorhandling/ErrorTypes/ValidationError.java @@ -0,0 +1,11 @@ +package com.nttdata.calender.errorhandling.ErrorTypes; + +import org.springframework.http.HttpStatus; + +public class ValidationError extends BackendError { + + public ValidationError(String message) { + super(message, 400, HttpStatus.BAD_REQUEST); + } + +} diff --git a/backend/src/main/java/com/nttdata/calender/errorhandling/GlobalExceptionHandler.java b/backend/src/main/java/com/nttdata/calender/errorhandling/GlobalExceptionHandler.java index 3d2cd00..9a7c7b0 100644 --- a/backend/src/main/java/com/nttdata/calender/errorhandling/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/nttdata/calender/errorhandling/GlobalExceptionHandler.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import com.bmc.arsys.api.ARException; +import com.nttdata.calender.errorhandling.ErrorTypes.BackendError; @ControllerAdvice public class GlobalExceptionHandler { @@ -18,17 +19,24 @@ public class GlobalExceptionHandler { @ExceptionHandler(ARException.class) public ResponseEntity handleARException(ARException e, HttpServletRequest request) { var errorMessage = "Remedy server error: \n" + e.getMessage(); - var errorResponse = new ErrorResponse(errorMessage, e.getClass().getSimpleName()); - - errorLogger.error(errorMessage, e); - return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); + return entityResponse(errorMessage, e, HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(Exception.class) public ResponseEntity handleGenericException(Exception e, HttpServletRequest request) { var errorMessage = "Backend internal server error: \n" + e.getMessage(); + return entityResponse(errorMessage, e, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(BackendError.class) + public ResponseEntity handleBackendErrorException(BackendError e, HttpServletRequest request) { + var errorMessage = "Backend internal server error: \n" + e.getMessage(); + return entityResponse(errorMessage, e, e.getHttpStatus()); + } + + private ResponseEntity entityResponse(String errorMessage, Exception e, HttpStatus status) { var errorResponse = new ErrorResponse(errorMessage, e.getClass().getSimpleName()); errorLogger.error(errorMessage, e); - return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>(errorResponse, status); } }