From 6d45b605ee9fbcc4c3f159a1fd95448440169bff Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Wed, 27 May 2026 16:11:24 -0500 Subject: [PATCH] MODWRKFLOW-54: Properly switch to Jackson 3. Use `JsonMapper` directly instead of `ObjectMapper`. - Use **Jackson 2** as the default behavior. - Apply common default properties. - Ensure modules are auto-found just like with **Jackson 2**. Address some of the problems reported by **SonarQube**. - Create `AbstractConverterFailure` to address **SonarQube** concerns about directly using `RuntimeException`. - Unit tests are not written and will likely have to be pending **SonarQube** report from upstream. - Make `Node` constructor protected as per **SonarQube**. Apply the newly established way of setting up the `JsonMapper`. Rename `objectMapper` uses to `mapper`. - This simplifies the code. - This removes the now inaccurate `object` reference from the name. - Not having `json` in the name will allow for this to be changed again in the future (if need be) without as many changes. --- .../exception/AbstractConverterFailure.java | 18 ++++++++++++ .../org/folio/rest/workflow/model/Node.java | 2 +- .../model/converter/AbstractConverter.java | 29 ++++++++++++------- .../DeserializeAsNodeJsonResolverTest.java | 6 ++-- .../workflow/config/KafkaProducerConfig.java | 2 +- .../workflow/controller/EventController.java | 10 +++---- .../controller/advice/AbstractAdvice.java | 10 +++---- .../advice/EventControllerAdvice.java | 26 +++++++++++++---- .../controller/advice/GlobalAdvice.java | 26 +++++++++++++---- .../advice/WorkflowControllerAdvice.java | 26 +++++++++++++---- .../workflow/service/AbstractCqlService.java | 6 ++-- .../service/OkapiDiscoveryService.java | 10 +++---- .../workflow/service/WorkflowCqlService.java | 4 +-- .../service/WorkflowEngineService.java | 4 +-- .../service/WorkflowImportService.java | 28 +++++++++--------- .../config/JunitHelperWebMvcConfig.java | 10 ++----- .../controller/ActionControllerTest.java | 4 +-- .../controller/WorkflowControllerTest.java | 4 +-- .../controller/advice/AbstractAdviceTest.java | 12 ++++---- ...overyServiceWithDefaultPropertiesTest.java | 4 +-- .../service/WorkflowCqlServiceTest.java | 15 +++++----- .../service/WorkflowEngineServiceTest.java | 6 ++-- .../service/WorkflowImportServiceTest.java | 4 +-- 23 files changed, 168 insertions(+), 98 deletions(-) create mode 100644 components/src/main/java/org/folio/rest/workflow/exception/AbstractConverterFailure.java diff --git a/components/src/main/java/org/folio/rest/workflow/exception/AbstractConverterFailure.java b/components/src/main/java/org/folio/rest/workflow/exception/AbstractConverterFailure.java new file mode 100644 index 00000000..7886e0d7 --- /dev/null +++ b/components/src/main/java/org/folio/rest/workflow/exception/AbstractConverterFailure.java @@ -0,0 +1,18 @@ +package org.folio.rest.workflow.exception; + +/** + * Failure in the AbstractConverter. + */ +public class AbstractConverterFailure extends RuntimeException { + + private static final long serialVersionUID = 1062422697623L; + + public AbstractConverterFailure(String message) { + super(message); + } + + public AbstractConverterFailure(String message, Exception e) { + super(message, e); + } + +} diff --git a/components/src/main/java/org/folio/rest/workflow/model/Node.java b/components/src/main/java/org/folio/rest/workflow/model/Node.java index 01e7d00c..a05d4c67 100644 --- a/components/src/main/java/org/folio/rest/workflow/model/Node.java +++ b/components/src/main/java/org/folio/rest/workflow/model/Node.java @@ -33,7 +33,7 @@ public abstract class Node extends AbstractBaseEntity implements HasId, HasInfor @Convert(converter = JsonNodeConverter.class, attributeName = "deserializeAs") private String deserializeAs = this.getClass().getSimpleName(); - public Node() { + protected Node() { super(); name = ""; diff --git a/components/src/main/java/org/folio/rest/workflow/model/converter/AbstractConverter.java b/components/src/main/java/org/folio/rest/workflow/model/converter/AbstractConverter.java index ba9ae40d..d6e17d14 100644 --- a/components/src/main/java/org/folio/rest/workflow/model/converter/AbstractConverter.java +++ b/components/src/main/java/org/folio/rest/workflow/model/converter/AbstractConverter.java @@ -1,12 +1,14 @@ package org.folio.rest.workflow.model.converter; +import com.fasterxml.jackson.annotation.JsonInclude; import jakarta.persistence.AttributeConverter; +import org.folio.rest.workflow.exception.AbstractConverterFailure; import tools.jackson.core.JacksonException; import tools.jackson.core.StreamReadFeature; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.MapperFeature; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.json.JsonMapper; /** @@ -17,11 +19,18 @@ */ public abstract class AbstractConverter implements AttributeConverter { - private ObjectMapper objectMapper = JsonMapper.builder() - .enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION) - .disable(MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES) - .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY) - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + private JsonMapper mapper = JsonMapper + .builderWithJackson2Defaults() + .configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES, true) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION, true) + .changeDefaultPropertyInclusion(incl -> incl + .withValueInclusion(JsonInclude.Include.NON_NULL) + .withContentInclusion(JsonInclude.Include.NON_NULL) + ) + .findAndAddModules() .build(); @Override @@ -29,9 +38,9 @@ public String convertToDatabaseColumn(T attribute) { if (attribute == null) return null; try { - return objectMapper.writeValueAsString(attribute); + return mapper.writeValueAsString(attribute); } catch (JacksonException e) { - throw new RuntimeException(e.getMessage()); + throw new AbstractConverterFailure(e.getMessage(), e); } } @@ -40,9 +49,9 @@ public T convertToEntityAttribute(String dbData) { if (dbData == null) return null; try { - return objectMapper.readValue(dbData, getTypeReference()); + return mapper.readValue(dbData, getTypeReference()); } catch (JacksonException e) { - throw new RuntimeException(e.getMessage()); + throw new AbstractConverterFailure(e.getMessage(), e); } } diff --git a/components/src/test/java/org/folio/rest/workflow/model/resolver/DeserializeAsNodeJsonResolverTest.java b/components/src/test/java/org/folio/rest/workflow/model/resolver/DeserializeAsNodeJsonResolverTest.java index 33a8887b..13018c4c 100644 --- a/components/src/test/java/org/folio/rest/workflow/model/resolver/DeserializeAsNodeJsonResolverTest.java +++ b/components/src/test/java/org/folio/rest/workflow/model/resolver/DeserializeAsNodeJsonResolverTest.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import java.util.stream.Stream; import java.util.stream.Stream.Builder; +import org.folio.spring.test.helper.MapperHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -21,13 +22,12 @@ import tools.jackson.databind.DatabindContext; import tools.jackson.databind.JavaType; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; @ExtendWith(MockitoExtension.class) class DeserializeAsNodeJsonResolverTest { - private ObjectMapper mapper; + private JsonMapper mapper; @Mock private DatabindContext databindContext; @@ -37,7 +37,7 @@ class DeserializeAsNodeJsonResolverTest { @BeforeEach void beforeEach() { - mapper = JsonMapper.builder().build(); + mapper = MapperHelper.build(); } @Test diff --git a/service/src/main/java/org/folio/rest/workflow/config/KafkaProducerConfig.java b/service/src/main/java/org/folio/rest/workflow/config/KafkaProducerConfig.java index 321c643f..cb97540b 100644 --- a/service/src/main/java/org/folio/rest/workflow/config/KafkaProducerConfig.java +++ b/service/src/main/java/org/folio/rest/workflow/config/KafkaProducerConfig.java @@ -22,7 +22,7 @@ public class KafkaProducerConfig { private String bootstrapAddress; @Bean - public ProducerFactory eventProducerFactory() { + ProducerFactory eventProducerFactory() { Map configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); diff --git a/service/src/main/java/org/folio/rest/workflow/controller/EventController.java b/service/src/main/java/org/folio/rest/workflow/controller/EventController.java index fe8caee1..da685f81 100644 --- a/service/src/main/java/org/folio/rest/workflow/controller/EventController.java +++ b/service/src/main/java/org/folio/rest/workflow/controller/EventController.java @@ -33,7 +33,7 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.HandlerMapping; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ObjectNode; @RestController @@ -55,13 +55,13 @@ public class EventController { private PathMatcher pathMatcher; - private ObjectMapper objectMapper; + private JsonMapper mapper; - public EventController(EventProducer eventProducer, TriggerRepo triggerRepo, PathMatcher pathMatcher, ObjectMapper objectMapper) { + public EventController(EventProducer eventProducer, TriggerRepo triggerRepo, PathMatcher pathMatcher, JsonMapper mapper) { this.eventProducer = eventProducer; this.triggerRepo = triggerRepo; this.pathMatcher = pathMatcher; - this.objectMapper = objectMapper; + this.mapper = mapper; } @PostMapping(value = "/**", consumes = "application/json", produces = { MediaType.APPLICATION_JSON_VALUE }) @@ -84,7 +84,7 @@ public JsonNode postHandleEventsWithFile( throw new FileSystemException("Invalid tenant directory name"); } - ObjectNode body = objectMapper.createObjectNode(); + ObjectNode body = mapper.createObjectNode(); Path tenantPath = Path.of(eventUploadsDirectory) .resolve(tenant) diff --git a/service/src/main/java/org/folio/rest/workflow/controller/advice/AbstractAdvice.java b/service/src/main/java/org/folio/rest/workflow/controller/advice/AbstractAdvice.java index c08d0fa7..3ea23a33 100644 --- a/service/src/main/java/org/folio/rest/workflow/controller/advice/AbstractAdvice.java +++ b/service/src/main/java/org/folio/rest/workflow/controller/advice/AbstractAdvice.java @@ -6,16 +6,16 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import tools.jackson.core.JacksonException; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; abstract class AbstractAdvice extends RequestMappingHandlerMapping { /** - * Get the object mapper + * Get the JSON mapper * - * @return objectMapper The object mapper. + * @return The JSON mapper. */ - protected abstract ObjectMapper getObjectMapper(); + protected abstract JsonMapper getMapper(); /** * Build the error message, with default JSON media type. @@ -46,7 +46,7 @@ protected ResponseEntity buildError(Exception ex, HttpStatus code, Medi // The exception handler should ideally not throw its own exceptions. // Catch the exceptions and report it, then fall back to a plain text error message. try { - message = getObjectMapper().writeValueAsString(ErrorUtility.buildError(ex, code)); + message = getMapper().writeValueAsString(ErrorUtility.buildError(ex, code)); } catch (JacksonException e) { logger.error("Mapping error to JSON Object failed.", e); diff --git a/service/src/main/java/org/folio/rest/workflow/controller/advice/EventControllerAdvice.java b/service/src/main/java/org/folio/rest/workflow/controller/advice/EventControllerAdvice.java index 39a9db5c..15443252 100644 --- a/service/src/main/java/org/folio/rest/workflow/controller/advice/EventControllerAdvice.java +++ b/service/src/main/java/org/folio/rest/workflow/controller/advice/EventControllerAdvice.java @@ -1,4 +1,5 @@ package org.folio.rest.workflow.controller.advice; +import com.fasterxml.jackson.annotation.JsonInclude; import java.nio.file.FileSystemException; import org.folio.rest.workflow.exception.EventPublishException; import org.springframework.http.HttpStatus; @@ -6,21 +7,36 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.core.StreamReadFeature; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.json.JsonMapper; @RestControllerAdvice public class EventControllerAdvice extends AbstractAdvice { - ObjectMapper objectMapper; + JsonMapper mapper; public EventControllerAdvice() { - this.objectMapper = JsonMapper.builder().build(); + this.mapper = JsonMapper + .builderWithJackson2Defaults() + .configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES, true) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION, true) + .changeDefaultPropertyInclusion(incl -> incl + .withValueInclusion(JsonInclude.Include.NON_NULL) + .withContentInclusion(JsonInclude.Include.NON_NULL) + ) + .findAndAddModules() + .build(); } @Override - protected ObjectMapper getObjectMapper() { - return objectMapper; + protected JsonMapper getMapper() { + return mapper; } @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) diff --git a/service/src/main/java/org/folio/rest/workflow/controller/advice/GlobalAdvice.java b/service/src/main/java/org/folio/rest/workflow/controller/advice/GlobalAdvice.java index 8f0342ff..9c59195d 100644 --- a/service/src/main/java/org/folio/rest/workflow/controller/advice/GlobalAdvice.java +++ b/service/src/main/java/org/folio/rest/workflow/controller/advice/GlobalAdvice.java @@ -1,25 +1,41 @@ package org.folio.rest.workflow.controller.advice; +import com.fasterxml.jackson.annotation.JsonInclude; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.TransactionSystemException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.core.StreamReadFeature; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.json.JsonMapper; @RestControllerAdvice public class GlobalAdvice extends AbstractAdvice { - ObjectMapper objectMapper; + JsonMapper mapper; public GlobalAdvice() { - this.objectMapper = JsonMapper.builder().build(); + this.mapper = JsonMapper + .builderWithJackson2Defaults() + .configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES, true) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION, true) + .changeDefaultPropertyInclusion(incl -> incl + .withValueInclusion(JsonInclude.Include.NON_NULL) + .withContentInclusion(JsonInclude.Include.NON_NULL) + ) + .findAndAddModules() + .build(); } @Override - protected ObjectMapper getObjectMapper() { - return objectMapper; + protected JsonMapper getMapper() { + return mapper; } @ExceptionHandler({ TransactionSystemException.class }) diff --git a/service/src/main/java/org/folio/rest/workflow/controller/advice/WorkflowControllerAdvice.java b/service/src/main/java/org/folio/rest/workflow/controller/advice/WorkflowControllerAdvice.java index 88cebeef..2027ddce 100644 --- a/service/src/main/java/org/folio/rest/workflow/controller/advice/WorkflowControllerAdvice.java +++ b/service/src/main/java/org/folio/rest/workflow/controller/advice/WorkflowControllerAdvice.java @@ -1,5 +1,6 @@ package org.folio.rest.workflow.controller.advice; +import com.fasterxml.jackson.annotation.JsonInclude; import jakarta.persistence.EntityNotFoundException; import org.folio.rest.workflow.exception.WorkflowAlreadyActiveException; import org.folio.rest.workflow.exception.WorkflowCreateAlreadyExistsException; @@ -12,21 +13,36 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.core.StreamReadFeature; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.SerializationFeature; import tools.jackson.databind.json.JsonMapper; @RestControllerAdvice public class WorkflowControllerAdvice extends AbstractAdvice { - ObjectMapper objectMapper; + JsonMapper mapper; public WorkflowControllerAdvice() { - this.objectMapper = JsonMapper.builder().build(); + this.mapper = JsonMapper + .builderWithJackson2Defaults() + .configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES, true) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) + .configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION, true) + .changeDefaultPropertyInclusion(incl -> incl + .withValueInclusion(JsonInclude.Include.NON_NULL) + .withContentInclusion(JsonInclude.Include.NON_NULL) + ) + .findAndAddModules() + .build(); } @Override - protected ObjectMapper getObjectMapper() { - return objectMapper; + protected JsonMapper getMapper() { + return mapper; } @ResponseStatus(HttpStatus.NOT_FOUND) diff --git a/service/src/main/java/org/folio/rest/workflow/service/AbstractCqlService.java b/service/src/main/java/org/folio/rest/workflow/service/AbstractCqlService.java index cea856d4..2fdf5535 100644 --- a/service/src/main/java/org/folio/rest/workflow/service/AbstractCqlService.java +++ b/service/src/main/java/org/folio/rest/workflow/service/AbstractCqlService.java @@ -1,7 +1,7 @@ package org.folio.rest.workflow.service; import java.util.List; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ArrayNode; import tools.jackson.databind.node.ObjectNode; @@ -18,9 +18,9 @@ public abstract class AbstractCqlService { public static final String TOTAL_RECORDS = "totalRecords"; /** - * The object mapper, which must be initialized in the extending class' constructor. + * The mapper, which must be initialized in the extending class' constructor. */ - protected ObjectMapper mapper; + protected JsonMapper mapper; /** * Use CQL to find a list of Workflows. diff --git a/service/src/main/java/org/folio/rest/workflow/service/OkapiDiscoveryService.java b/service/src/main/java/org/folio/rest/workflow/service/OkapiDiscoveryService.java index c874b446..194543f2 100644 --- a/service/src/main/java/org/folio/rest/workflow/service/OkapiDiscoveryService.java +++ b/service/src/main/java/org/folio/rest/workflow/service/OkapiDiscoveryService.java @@ -18,7 +18,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; @Service public class OkapiDiscoveryService { @@ -44,11 +44,11 @@ public class OkapiDiscoveryService { private HttpService httpService; - private ObjectMapper objectMapper; + private JsonMapper mapper; - public OkapiDiscoveryService(HttpService httpService, ObjectMapper objectMapper) { + public OkapiDiscoveryService(HttpService httpService, JsonMapper mapper) { this.httpService = httpService; - this.objectMapper = objectMapper; + this.mapper = mapper; } public List getActionsByTenant(String tenant) { @@ -82,7 +82,7 @@ public Map> getHandlers(String tenant, String id) { List handlers = new ArrayList<>(); String interfaceName = interfaceNode.get(ID).asString(); for (JsonNode handlersNode : interfaceNode.get(HANDLERS)) { - handlers.add(objectMapper.readValue(handlersNode.toString(), Handler.class)); + handlers.add(mapper.readValue(handlersNode.toString(), Handler.class)); } handlerMap.put(interfaceName, handlers); } diff --git a/service/src/main/java/org/folio/rest/workflow/service/WorkflowCqlService.java b/service/src/main/java/org/folio/rest/workflow/service/WorkflowCqlService.java index dbb0cea8..ddb8828f 100644 --- a/service/src/main/java/org/folio/rest/workflow/service/WorkflowCqlService.java +++ b/service/src/main/java/org/folio/rest/workflow/service/WorkflowCqlService.java @@ -6,7 +6,7 @@ import org.folio.spring.data.OffsetRequest; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ObjectNode; @Service @@ -14,7 +14,7 @@ public class WorkflowCqlService extends AbstractCqlService { private WorkflowRepo repo; - public WorkflowCqlService(ObjectMapper mapper, WorkflowRepo repo) { + public WorkflowCqlService(JsonMapper mapper, WorkflowRepo repo) { this.mapper = mapper; this.repo = repo; } diff --git a/service/src/main/java/org/folio/rest/workflow/service/WorkflowEngineService.java b/service/src/main/java/org/folio/rest/workflow/service/WorkflowEngineService.java index 78cc72c7..1a0143f5 100644 --- a/service/src/main/java/org/folio/rest/workflow/service/WorkflowEngineService.java +++ b/service/src/main/java/org/folio/rest/workflow/service/WorkflowEngineService.java @@ -20,7 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ArrayNode; import tools.jackson.databind.node.ObjectNode; @@ -57,7 +57,7 @@ public class WorkflowEngineService { private WorkflowRepo workflowRepo; @Autowired - private ObjectMapper mapper; + private JsonMapper mapper; private RestTemplate restTemplate; diff --git a/service/src/main/java/org/folio/rest/workflow/service/WorkflowImportService.java b/service/src/main/java/org/folio/rest/workflow/service/WorkflowImportService.java index 81fc2541..b4ca6ebe 100644 --- a/service/src/main/java/org/folio/rest/workflow/service/WorkflowImportService.java +++ b/service/src/main/java/org/folio/rest/workflow/service/WorkflowImportService.java @@ -55,7 +55,7 @@ import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ArrayNode; import tools.jackson.databind.node.JsonNodeType; import tools.jackson.databind.node.ObjectNode; @@ -65,7 +65,7 @@ public class WorkflowImportService { private static final Log LOG = LogFactory.getLog(WorkflowImportService.class); - private ObjectMapper objectMapper; + private JsonMapper mapper; private NodeRepo nodeRepo; @@ -73,8 +73,8 @@ public class WorkflowImportService { private WorkflowRepo workflowRepo; - public WorkflowImportService(ObjectMapper objectMapper, NodeRepo nodeRepo, TriggerRepo triggerRepo, WorkflowRepo workflowRepo) { - this.objectMapper = objectMapper; + public WorkflowImportService(JsonMapper mapper, NodeRepo nodeRepo, TriggerRepo triggerRepo, WorkflowRepo workflowRepo) { + this.mapper = mapper; this.nodeRepo = nodeRepo; this.triggerRepo = triggerRepo; this.workflowRepo = workflowRepo; @@ -169,7 +169,7 @@ private void collapseNodeScripts(ExtractedWorkflow extracted) throws WorkflowImp throw new WorkflowImportRequiredFileMissing(scriptPath); } - String stringified = objectMapper.writeValueAsString(extracted.getScripts().get(scriptPath)); + String stringified = mapper.writeValueAsString(extracted.getScripts().get(scriptPath)); ((ObjectNode) entry.getValue()).put(CODE, stringified); } @@ -209,7 +209,7 @@ private boolean collapseNodeScriptsContinue(Entry entry) throw */ private void createNodes(Map nodes, List expanded) { for (String uuid : expanded) { - Node node = objectMapper.readValue(nodes.get(uuid).toString(), Node.class); + Node node = mapper.readValue(nodes.get(uuid).toString(), Node.class); nodeRepo.save(node); } } @@ -221,7 +221,7 @@ private void createNodes(Map nodes, List expanded) { */ private void createTriggers(Map triggers) { for (JsonNode triggerNode : triggers.values()) { - Trigger trigger = objectMapper.readValue(triggerNode.toString(), Trigger.class); + Trigger trigger = mapper.readValue(triggerNode.toString(), Trigger.class); triggerRepo.save(trigger); } } @@ -234,7 +234,7 @@ private void createTriggers(Map triggers) { * @return The created Workflow. */ private Workflow createWorkflow(JsonNode workflowJson) { - Workflow workflow = objectMapper.readValue(workflowJson.toString(), Workflow.class); + Workflow workflow = mapper.readValue(workflowJson.toString(), Workflow.class); return workflowRepo.save(workflow); } @@ -292,13 +292,13 @@ private void extractNodesAndScripts(String name, InputStream inputStream, Extrac */ private void extractTopLevel(String name, InputStream inputStream, ExtractedWorkflow extracted) { if (FWZ_JSON.equalsIgnoreCase(name)) { - extracted.getRequired().put(FWZ_JSON, objectMapper.readTree(inputStream)); + extracted.getRequired().put(FWZ_JSON, mapper.readTree(inputStream)); verifyVersion(extracted.getRequired().get(FWZ_JSON)); } else if (WORKFLOW_JSON.equalsIgnoreCase(name)) { - extracted.getRequired().put(WORKFLOW_JSON, objectMapper.readTree(inputStream)); + extracted.getRequired().put(WORKFLOW_JSON, mapper.readTree(inputStream)); } else if (SETUP_JSON.equalsIgnoreCase(name)) { - extracted.getRequired().put(SETUP_JSON, objectMapper.readTree(inputStream)); + extracted.getRequired().put(SETUP_JSON, mapper.readTree(inputStream)); } else { warnOnUnknownFileOrDir(name); } @@ -317,7 +317,7 @@ private void extractTopLevel(String name, InputStream inputStream, ExtractedWork */ private void extractSubLevel(String name, InputStream inputStream, ExtractedWorkflow extracted, String[] pathParts) throws WorkflowImportInvalidOrMissingProperty { - JsonNode json = objectMapper.readTree(inputStream); + JsonNode json = mapper.readTree(inputStream); if (!json.has(ID) || json.get(ID).getNodeType() != JsonNodeType.STRING) { throw new WorkflowImportInvalidOrMissingProperty(name, ID); } @@ -381,8 +381,8 @@ private void expandNode(Map nodes, JsonNode node, List throw new WorkflowImportInvalidOrMissingProperty(nodeId, NODES); } - ArrayNode expandedNodes = objectMapper.createArrayNode(); - String[] values = objectMapper.readValue(node.get(NODES).toString(), String[].class); + ArrayNode expandedNodes = mapper.createArrayNode(); + String[] values = mapper.readValue(node.get(NODES).toString(), String[].class); for (String value : values) { String[] parts = value.split("/"); diff --git a/service/src/test/java/org/folio/rest/workflow/config/JunitHelperWebMvcConfig.java b/service/src/test/java/org/folio/rest/workflow/config/JunitHelperWebMvcConfig.java index 18fcea7d..f7400809 100644 --- a/service/src/test/java/org/folio/rest/workflow/config/JunitHelperWebMvcConfig.java +++ b/service/src/test/java/org/folio/rest/workflow/config/JunitHelperWebMvcConfig.java @@ -1,10 +1,9 @@ package org.folio.rest.workflow.config; +import org.folio.spring.test.helper.MapperHelper; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.test.context.ActiveProfiles; -import tools.jackson.databind.DeserializationFeature; -import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; /** @@ -15,10 +14,7 @@ public class JunitHelperWebMvcConfig { @Bean - ObjectMapper objectMapper() { - return JsonMapper - .builder() - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .build(); + JsonMapper jsonMapper() { + return MapperHelper.build(); } } diff --git a/service/src/test/java/org/folio/rest/workflow/controller/ActionControllerTest.java b/service/src/test/java/org/folio/rest/workflow/controller/ActionControllerTest.java index 0fc85616..be2895c6 100644 --- a/service/src/test/java/org/folio/rest/workflow/controller/ActionControllerTest.java +++ b/service/src/test/java/org/folio/rest/workflow/controller/ActionControllerTest.java @@ -56,7 +56,7 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.util.MultiValueMap; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; @WebMvcTest(ActionController.class) @ExtendWith(MockitoExtension.class) @@ -67,7 +67,7 @@ class ActionControllerTest { private MockMvc mvc; @Autowired - protected ObjectMapper mapper; + protected JsonMapper mapper; @Autowired private ActionController actionController; diff --git a/service/src/test/java/org/folio/rest/workflow/controller/WorkflowControllerTest.java b/service/src/test/java/org/folio/rest/workflow/controller/WorkflowControllerTest.java index 24fc251c..c0181c8c 100644 --- a/service/src/test/java/org/folio/rest/workflow/controller/WorkflowControllerTest.java +++ b/service/src/test/java/org/folio/rest/workflow/controller/WorkflowControllerTest.java @@ -79,7 +79,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ObjectNode; @WebMvcTest(WorkflowController.class) @@ -160,7 +160,7 @@ class WorkflowControllerTest { private MockMvc mvc; @Autowired - protected ObjectMapper mapper; + protected JsonMapper mapper; @Autowired private WorkflowController workflowController; diff --git a/service/src/test/java/org/folio/rest/workflow/controller/advice/AbstractAdviceTest.java b/service/src/test/java/org/folio/rest/workflow/controller/advice/AbstractAdviceTest.java index 268c9803..a26c6409 100644 --- a/service/src/test/java/org/folio/rest/workflow/controller/advice/AbstractAdviceTest.java +++ b/service/src/test/java/org/folio/rest/workflow/controller/advice/AbstractAdviceTest.java @@ -4,6 +4,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import org.folio.spring.test.helper.MapperHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -14,7 +15,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import tools.jackson.core.JacksonException; -import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; @ExtendWith(MockitoExtension.class) @@ -22,13 +22,13 @@ class AbstractAdviceTest { private static final RuntimeException runtimeException = new RuntimeException("A runtime failure."); - private ObjectMapper objectMapper; + private JsonMapper mapper; private MockAdvice abstractAdvice; @BeforeEach void beforeEach() { - objectMapper = Mockito.spy(JsonMapper.builder().build()); + mapper = Mockito.spy(MapperHelper.build()); abstractAdvice = new MockAdvice(); } @@ -42,7 +42,7 @@ void handleBuildErrorWorksTest() { @Test void handleBuildErrorThrowsJsonProcessingExceptionTest() throws JacksonException { - when(objectMapper.writeValueAsString(any())).thenThrow(new MockJacksonException()); + when(mapper.writeValueAsString(any())).thenThrow(new MockJacksonException()); ResponseEntity response = abstractAdvice.handleException(runtimeException); @@ -58,8 +58,8 @@ void handleBuildErrorThrowsJsonProcessingExceptionTest() throws JacksonException private class MockAdvice extends AbstractAdvice { @Override - protected ObjectMapper getObjectMapper() { - return objectMapper; + protected JsonMapper getMapper() { + return mapper; } @ExceptionHandler({ RuntimeException.class }) diff --git a/service/src/test/java/org/folio/rest/workflow/service/OkapiDiscoveryServiceWithDefaultPropertiesTest.java b/service/src/test/java/org/folio/rest/workflow/service/OkapiDiscoveryServiceWithDefaultPropertiesTest.java index bc570abf..3216a21b 100644 --- a/service/src/test/java/org/folio/rest/workflow/service/OkapiDiscoveryServiceWithDefaultPropertiesTest.java +++ b/service/src/test/java/org/folio/rest/workflow/service/OkapiDiscoveryServiceWithDefaultPropertiesTest.java @@ -23,7 +23,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; /** * This runs the OkapiDiscoveryService tests for when there is no properly configuration application.yml. @@ -32,7 +32,7 @@ */ @ActiveProfiles("test") @SpringJUnitWebConfig({ - ObjectMapper.class, + JsonMapper.class, OkapiDiscoveryService.class, HttpService.class }) diff --git a/service/src/test/java/org/folio/rest/workflow/service/WorkflowCqlServiceTest.java b/service/src/test/java/org/folio/rest/workflow/service/WorkflowCqlServiceTest.java index 5bc605dd..03f450af 100644 --- a/service/src/test/java/org/folio/rest/workflow/service/WorkflowCqlServiceTest.java +++ b/service/src/test/java/org/folio/rest/workflow/service/WorkflowCqlServiceTest.java @@ -6,12 +6,12 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; -import java.io.IOException; import java.util.ArrayList; import java.util.stream.Stream; import org.folio.rest.workflow.model.Workflow; import org.folio.rest.workflow.model.repo.WorkflowRepo; import org.folio.spring.data.OffsetRequest; +import org.folio.spring.test.helper.MapperHelper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -26,7 +26,6 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.context.junit.jupiter.SpringExtension; -import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ObjectNode; @@ -44,9 +43,9 @@ class WorkflowCqlServiceTest { @MockitoBean private WorkflowRepo repo; - // Work-around to get ObjectMapper to be loaded in AbstractCqlService. + // Work-around to get mapper to be loaded in AbstractCqlService. @MockitoSpyBean - protected ObjectMapper mapper; + protected JsonMapper mapper; @Mock private Page page; @@ -56,13 +55,13 @@ class WorkflowCqlServiceTest { static class Config { @Bean - ObjectMapper objectMapper() { - return JsonMapper.builder().build(); + JsonMapper objectMapper() { + return MapperHelper.build(); } } @Test - void getTypeNameWorksTest() throws IOException { + void getTypeNameWorksTest() { final String name = workflowCqlService.getTypeName(); assertEquals(Workflow.class.getSimpleName().toLowerCase() + "s", name); @@ -70,7 +69,7 @@ void getTypeNameWorksTest() throws IOException { @ParameterizedTest @MethodSource("provideFindByCql") - void findByCqlWorksTest(String query, Long offset, Integer limit, String expect) throws IOException { + void findByCqlWorksTest(String query, Long offset, Integer limit, String expect) { when(repo.findAll(any(OffsetRequest.class))).thenReturn(page); when(repo.findByCql(anyString(), any(OffsetRequest.class))).thenReturn(page); when(page.toList()).thenReturn(new ArrayList()); diff --git a/service/src/test/java/org/folio/rest/workflow/service/WorkflowEngineServiceTest.java b/service/src/test/java/org/folio/rest/workflow/service/WorkflowEngineServiceTest.java index 3a2d25dc..95d99a25 100644 --- a/service/src/test/java/org/folio/rest/workflow/service/WorkflowEngineServiceTest.java +++ b/service/src/test/java/org/folio/rest/workflow/service/WorkflowEngineServiceTest.java @@ -23,6 +23,7 @@ import org.folio.rest.workflow.exception.WorkflowNotFoundException; import org.folio.rest.workflow.model.Workflow; import org.folio.rest.workflow.model.repo.WorkflowRepo; +import org.folio.spring.test.helper.MapperHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -37,7 +38,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.web.client.RestTemplate; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ArrayNode; import tools.jackson.databind.node.ObjectNode; @@ -58,12 +58,12 @@ class WorkflowEngineServiceTest { private WorkflowEngineService workflowEngineService; - private ObjectMapper mapper; + private JsonMapper mapper; @BeforeEach void beforeEach() { workflowEngineService = new WorkflowEngineService(new RestTemplateBuilder()); - mapper = JsonMapper.builder().build(); + mapper = MapperHelper.build(); workflow = new WorkflowAsDto(); workflow.setId(UUID); diff --git a/service/src/test/java/org/folio/rest/workflow/service/WorkflowImportServiceTest.java b/service/src/test/java/org/folio/rest/workflow/service/WorkflowImportServiceTest.java index eb53d31b..76409c5d 100644 --- a/service/src/test/java/org/folio/rest/workflow/service/WorkflowImportServiceTest.java +++ b/service/src/test/java/org/folio/rest/workflow/service/WorkflowImportServiceTest.java @@ -31,7 +31,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; @ActiveProfiles("test") @SpringJUnitWebConfig({ @@ -57,7 +57,7 @@ class WorkflowImportServiceTest { private WorkflowRepo workflowRepo; @MockitoSpyBean - private ObjectMapper objectMapper; + private JsonMapper mapper; @Mock private Page page;