From ae6dde0224f66f82d86121a711d972cca199670a Mon Sep 17 00:00:00 2001 From: zeyad2003 Date: Wed, 27 Mar 2024 11:32:09 +0200 Subject: [PATCH] FINERACT-1269: (fix) add a try-catch block to ensure that the json is deserialized successfully. - Wrap the fromJson() method with try-catch block and throw proper exception. - Add `publishHookEventHandlesInvalidJson()` method to test the invalid json is handled successfully. --- .../SynchronousCommandProcessingService.java | 89 +++++++++++-------- ...nchronousCommandProcessingServiceTest.java | 19 +++- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java index 6b3a6ae8839..3df570b1fa3 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java @@ -27,6 +27,7 @@ import io.github.resilience4j.retry.annotation.Retry; import java.lang.reflect.Type; import java.time.Instant; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -47,6 +48,7 @@ import org.apache.fineract.infrastructure.core.exception.IdempotentCommandProcessFailedException; import org.apache.fineract.infrastructure.core.exception.IdempotentCommandProcessSucceedException; import org.apache.fineract.infrastructure.core.exception.IdempotentCommandProcessUnderProcessingException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper; import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; @@ -261,56 +263,67 @@ public boolean validateRollbackCommand(final CommandWrapper commandWrapper, fina return isMakerChecker && !user.isCheckerSuperUser(); } - private void publishHookEvent(final String entityName, final String actionName, JsonCommand command, final Object result) { - try { - final AppUser appUser = context.authenticatedUser(CommandWrapper.wrap(actionName, entityName, null, null)); - - final HookEventSource hookEventSource = new HookEventSource(entityName, actionName); + protected void publishHookEvent(final String entityName, final String actionName, JsonCommand command, final Object result) { - // TODO: Add support for publishing array events - if (command.json() != null && command.json().startsWith("{")) { - Type type = new TypeToken>() { + final AppUser appUser = context.authenticatedUser(CommandWrapper.wrap(actionName, entityName, null, null)); - }.getType(); - Map myMap = gson.fromJson(command.json(), type); + final HookEventSource hookEventSource = new HookEventSource(entityName, actionName); - Map reqmap = new HashMap<>(); - reqmap.put("entityName", entityName); - reqmap.put("actionName", actionName); - reqmap.put("createdBy", context.authenticatedUser().getId()); - reqmap.put("createdByName", context.authenticatedUser().getUsername()); - reqmap.put("createdByFullName", context.authenticatedUser().getDisplayName()); + // TODO: Add support for publishing array events + if (command.json() != null) { + Type type = new TypeToken>() { - reqmap.put("request", myMap); - if (result instanceof CommandProcessingResult) { - CommandProcessingResult resultCopy = CommandProcessingResult - .fromCommandProcessingResult((CommandProcessingResult) result); + }.getType(); - reqmap.put("officeId", resultCopy.getOfficeId()); - reqmap.put("clientId", resultCopy.getClientId()); - resultCopy.setOfficeId(null); - reqmap.put("response", resultCopy); - } else if (result instanceof ErrorInfo ex) { - reqmap.put("status", "Exception"); + Map myMap; - Map errorMap = gson.fromJson(ex.getMessage(), type); - errorMap.put("errorCode", ex.getErrorCode()); - errorMap.put("statusCode", ex.getStatusCode()); + try { + myMap = gson.fromJson(command.json(), type); + } catch (Exception e) { + throw new PlatformApiDataValidationException("error.msg.invalid.json", "The provided JSON is invalid.", new ArrayList<>(), + e); + } - reqmap.put("response", errorMap); + Map reqmap = new HashMap<>(); + reqmap.put("entityName", entityName); + reqmap.put("actionName", actionName); + reqmap.put("createdBy", context.authenticatedUser().getId()); + reqmap.put("createdByName", context.authenticatedUser().getUsername()); + reqmap.put("createdByFullName", context.authenticatedUser().getDisplayName()); + + reqmap.put("request", myMap); + if (result instanceof CommandProcessingResult) { + CommandProcessingResult resultCopy = CommandProcessingResult.fromCommandProcessingResult((CommandProcessingResult) result); + + reqmap.put("officeId", resultCopy.getOfficeId()); + reqmap.put("clientId", resultCopy.getClientId()); + resultCopy.setOfficeId(null); + reqmap.put("response", resultCopy); + } else if (result instanceof ErrorInfo ex) { + reqmap.put("status", "Exception"); + + Map errorMap = new HashMap<>(); + + try { + errorMap = gson.fromJson(ex.getMessage(), type); + } catch (Exception e) { + errorMap.put("errorMessage", ex.getMessage()); } - reqmap.put("timestamp", Instant.now().toString()); + errorMap.put("errorCode", ex.getErrorCode()); + errorMap.put("statusCode", ex.getStatusCode()); - final String serializedResult = toApiResultJsonSerializer.serialize(reqmap); + reqmap.put("response", errorMap); + } - final HookEvent applicationEvent = new HookEvent(hookEventSource, serializedResult, appUser, - ThreadLocalContextUtil.getContext()); + reqmap.put("timestamp", Instant.now().toString()); - applicationContext.publishEvent(applicationEvent); - } - } catch (Exception e) { - log.error("Error", e); + final String serializedResult = toApiResultJsonSerializer.serialize(reqmap); + + final HookEvent applicationEvent = new HookEvent(hookEventSource, serializedResult, appUser, + ThreadLocalContextUtil.getContext()); + + applicationContext.publishEvent(applicationEvent); } } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/commands/service/SynchronousCommandProcessingServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/commands/service/SynchronousCommandProcessingServiceTest.java index 4e6e0c43649..361d0361e29 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/commands/service/SynchronousCommandProcessingServiceTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/commands/service/SynchronousCommandProcessingServiceTest.java @@ -19,6 +19,7 @@ package org.apache.fineract.commands.service; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,10 +35,10 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.useradministration.domain.AppUser; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -163,11 +164,25 @@ public void testExecuteCommandFails() { when(commandSourceService.processCommand(commandHandler, jsonCommand, commandSource, appUser, false, false)) .thenThrow(runtimeException); - Assertions.assertThrows(RuntimeException.class, () -> { + assertThrows(RuntimeException.class, () -> { underTest.executeCommand(commandWrapper, jsonCommand, false); }); verify(commandSourceService).getCommandSource(commandId); verify(commandSourceService).generateErrorInfo(runtimeException); } + + @Test + public void publishHookEventHandlesInvalidJson() { + String entityName = "entity"; + String actionName = "action"; + JsonCommand command = Mockito.mock(JsonCommand.class); + String invalidJson = "{ invalidJson }"; + + when(command.json()).thenReturn(invalidJson); + + assertThrows(PlatformApiDataValidationException.class, () -> { + underTest.publishHookEvent(entityName, actionName, command, Object.class); + }); + } }