diff --git a/pom.xml b/pom.xml index 77ffea8..4861015 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ 21 3.2.0 - 4.0.6 + 4.0.7 3.5.3 @@ -28,10 +28,11 @@ false 3.3.1 3.7.1 - 7.0.7 + 7.0.8 3.19.0 - 11.0.22 + 11.0.24 5.14.1 + 1.42.0 3.0.57 @@ -61,6 +62,7 @@ 2.26.0 32.0.1-android 1.81.0 + 3.1.4 @@ -143,6 +145,19 @@ ${grpc-context.version} compile + + tools.jackson.core + jackson-core + ${jackson-core.version} + compile + + + + io.opentelemetry.semconv + opentelemetry-semconv + ${opentelemetry-semconv.version} + + @@ -187,6 +202,10 @@ org.apache.commons commons-lang3 + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + @@ -220,23 +239,63 @@ org.apache.commons commons-lang3 + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + uk.gov.companieshouse api-helper-java ${api-helper-java-library.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + com.github.docker-java docker-java-api ${docker-java-api.version} compile + + + com.fasterxml.jackson.core + jackson-annotations + + org.apache.commons commons-lang3 ${commons-lang3.version} + + + com.fasterxml.jackson.core + jackson-annotations + + + + + tools.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + diff --git a/src/main/java/uk/gov/companieshouse/api/converter/ReadConverter.java b/src/main/java/uk/gov/companieshouse/api/converter/ReadConverter.java index 2f3c8ba..db30df7 100644 --- a/src/main/java/uk/gov/companieshouse/api/converter/ReadConverter.java +++ b/src/main/java/uk/gov/companieshouse/api/converter/ReadConverter.java @@ -4,10 +4,8 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; -import uk.gov.companieshouse.api.exception.InternalServiceException; @ReadingConverter public class ReadConverter implements Converter { @@ -22,10 +20,6 @@ public ReadConverter(ObjectMapper objectMapper, Class objectClass) { @Override public T convert(Document source) { - try { - return this.objectMapper.readValue(source.toJson(), objectClass); - } catch (JsonProcessingException e) { - throw new InternalServiceException("failed to read and convert Document to JSON", e); - } + return this.objectMapper.readValue(source.toJson(), objectClass); } } diff --git a/src/main/java/uk/gov/companieshouse/api/converter/WriteConverter.java b/src/main/java/uk/gov/companieshouse/api/converter/WriteConverter.java index d3278bc..1de2719 100644 --- a/src/main/java/uk/gov/companieshouse/api/converter/WriteConverter.java +++ b/src/main/java/uk/gov/companieshouse/api/converter/WriteConverter.java @@ -1,7 +1,6 @@ package uk.gov.companieshouse.api.converter; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import com.mongodb.BasicDBObject; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.WritingConverter; @@ -21,10 +20,6 @@ public WriteConverter(ObjectMapper objectMapper) { */ @Override public BasicDBObject convert(S source) { - try { - return BasicDBObject.parse(objectMapper.writeValueAsString(source)); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException(e); - } + return BasicDBObject.parse(objectMapper.writeValueAsString(source)); } } diff --git a/src/main/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResult.java b/src/main/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResult.java index 1fe863b..d7fe43a 100644 --- a/src/main/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResult.java +++ b/src/main/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResult.java @@ -30,10 +30,9 @@ public void setKind(TransactionKind kind) { @Override public boolean equals(Object obj) { - if (!(obj instanceof TransactionKindResult)) { + if (!(obj instanceof TransactionKindResult cmp)) { return false; } - TransactionKindResult cmp = (TransactionKindResult) obj; final boolean sameKind = (this.kind == null && cmp.kind == null) || (this.kind != null && this.kind.equals(cmp.kind)); diff --git a/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializer.java b/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializer.java index 634afc5..382573f 100644 --- a/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializer.java +++ b/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializer.java @@ -1,34 +1,49 @@ package uk.gov.companieshouse.api.serialization; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.JsonNode; import java.time.ZoneOffset; import uk.gov.companieshouse.api.exception.BadRequestException; - import java.time.Instant; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -public class LocalDateDeserializer extends JsonDeserializer { +public class LocalDateDeserializer extends ValueDeserializer { @Override - public LocalDate deserialize(JsonParser jsonParser, DeserializationContext - deserializationContext) { + public LocalDate deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) { + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); + JsonNode dateNode; try { - DateTimeFormatter dateTimeFormatter = DateTimeFormatter - .ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); - JsonNode jsonNode = jsonParser.readValueAsTree(); - JsonNode dateNode = jsonNode.get("$date"); + dateNode = ((JsonNode) jsonParser.readValueAsTree()).get("$date"); + } catch (Exception e) { + throw new BadRequestException("Deserialization failed.", e); + } - return dateNode.textValue() != null ? - LocalDate.parse(dateNode.textValue(), dateTimeFormatter) : - Instant.ofEpochMilli(dateNode.get("$numberLong").asLong()) - .atZone(ZoneOffset.UTC).toLocalDate(); + if (dateNode == null || dateNode.isNull()) { + throw new BadRequestException("$date field is missing or null"); + } - } catch (Exception exception) { - throw new BadRequestException("Deserialization failed.", exception); + if (dateNode.isString()) { + try { + return LocalDate.parse(dateNode.stringValue(), fmt); + } catch (Exception e) { + throw new BadRequestException("Deserialization failed.", e); + } } - } -} + if (dateNode.isObject() && dateNode.has("$numberLong")) { + try { + return Instant.ofEpochMilli(Long.parseLong(dateNode.get("$numberLong").stringValue())) + .atZone(ZoneOffset.UTC) + .toLocalDate(); + } catch (Exception e) { + throw new BadRequestException("Deserialization failed.", e); + } + } + + throw new BadRequestException("Unrecognized $date format"); + } +} \ No newline at end of file diff --git a/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateSerializer.java b/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateSerializer.java index bbdbfb9..8f51fbd 100644 --- a/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateSerializer.java +++ b/src/main/java/uk/gov/companieshouse/api/serialization/LocalDateSerializer.java @@ -1,24 +1,21 @@ package uk.gov.companieshouse.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.SerializationContext; +import tools.jackson.core.JsonGenerator; -import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -public class LocalDateSerializer extends JsonSerializer { - +public class LocalDateSerializer extends ValueSerializer { @Override - public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, - SerializerProvider serializerProvider) throws IOException { - if (localDate == null) { - jsonGenerator.writeNull(); + public void serialize(LocalDate value, JsonGenerator gen, SerializationContext ctxt) { + if (value == null) { + gen.writeNull(); } else { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - String format = localDate.atStartOfDay().format(dtf); - jsonGenerator.writeRawValue("ISODate(\"" + format + "\")"); + String format = value.atStartOfDay().format(dtf); + gen.writeRawValue("ISODate(\"" + format + "\")"); } } -} +} \ No newline at end of file diff --git a/src/test/java/uk/gov/companieshouse/api/converter/ReadConverterTest.java b/src/test/java/uk/gov/companieshouse/api/converter/ReadConverterTest.java index 5d25427..769ef28 100644 --- a/src/test/java/uk/gov/companieshouse/api/converter/ReadConverterTest.java +++ b/src/test/java/uk/gov/companieshouse/api/converter/ReadConverterTest.java @@ -1,14 +1,13 @@ package uk.gov.companieshouse.api.converter; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.core.JacksonException; import org.bson.Document; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import uk.gov.companieshouse.api.exception.InternalServiceException; +import tools.jackson.databind.ObjectMapper; import uk.gov.companieshouse.api.psc.Statement; import uk.gov.companieshouse.api.utils.TestHelper; @@ -35,19 +34,19 @@ void correctlyConvertsDocumentToStatementObject(){ } @Test - void testConvertThrowsInternalServiceExceptionOnJsonError() throws Exception { + void testConvertThrowsInternalServiceExceptionOnJsonError() { // GIVEN ObjectMapper objectMapper = mock(ObjectMapper.class); Document source = new Document(); when(objectMapper.readValue(anyString(), ArgumentMatchers.>any())) - .thenThrow(new JsonProcessingException("Error") {}); + .thenThrow(new JacksonException("Error") {}); // WHEN ReadConverter converter = new ReadConverter<>(objectMapper, Statement.class); // THEN - Exception ex = Assertions.assertThrows(InternalServiceException.class, () -> converter.convert(source)); - Assertions.assertEquals("failed to read and convert Document to JSON", ex.getMessage()); + Exception ex = Assertions.assertThrows(JacksonException.class, () -> converter.convert(source)); + Assertions.assertEquals("Error", ex.getMessage()); } -} +} \ No newline at end of file diff --git a/src/test/java/uk/gov/companieshouse/api/converter/WriteConverterTest.java b/src/test/java/uk/gov/companieshouse/api/converter/WriteConverterTest.java index 74635ed..d4c8128 100644 --- a/src/test/java/uk/gov/companieshouse/api/converter/WriteConverterTest.java +++ b/src/test/java/uk/gov/companieshouse/api/converter/WriteConverterTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import com.mongodb.BasicDBObject; import uk.gov.companieshouse.api.psc.Statement; diff --git a/src/test/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResultTest.java b/src/test/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResultTest.java new file mode 100644 index 0000000..e60afbd --- /dev/null +++ b/src/test/java/uk/gov/companieshouse/api/filinghistory/utils/TransactionKindResultTest.java @@ -0,0 +1,92 @@ +package uk.gov.companieshouse.api.filinghistory.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class TransactionKindResultTest { + + @Test + void constructorShouldSetFields() { + TransactionKind kind = TransactionKind.TOP_LEVEL; + + TransactionKindResult result = new TransactionKindResult("encoded-id", kind); + + assertEquals("encoded-id", result.getEncodedId()); + assertEquals(kind, result.getKind()); + } + + @Test + void settersShouldUpdateFields() { + TransactionKindResult result = new TransactionKindResult(null, null); + + result.setEncodedId("new-id"); + result.setKind(TransactionKind.TOP_LEVEL); + + assertEquals("new-id", result.getEncodedId()); + assertEquals(TransactionKind.TOP_LEVEL, result.getKind()); + } + + @Test + void equalsShouldReturnTrueForSameValues() { + TransactionKindResult first = new TransactionKindResult("id1", TransactionKind.TOP_LEVEL); + + TransactionKindResult second = new TransactionKindResult("id1", TransactionKind.TOP_LEVEL); + + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + } + + @Test + void equalsShouldReturnFalseForDifferentEncodedId() { + TransactionKindResult first = new TransactionKindResult("id1", TransactionKind.TOP_LEVEL); + + TransactionKindResult second = new TransactionKindResult("id2", TransactionKind.TOP_LEVEL); + + assertNotEquals(first, second); + assertNotEquals(first.hashCode(), second.hashCode()); + } + + @Test + void equalsShouldReturnFalseForDifferentKind() { + TransactionKindResult first = new TransactionKindResult("id1", TransactionKind.TOP_LEVEL); + + TransactionKindResult second = new TransactionKindResult("id1", TransactionKind.ASSOCIATED_FILING); + + assertNotEquals(first, second); + assertNotEquals(first.hashCode(), second.hashCode()); + } + + @Test + void equalsShouldReturnFalseForDifferentObjectType() { + TransactionKindResult result = new TransactionKindResult("id1", TransactionKind.ANNOTATION); + + assertNotEquals("not-a-TransactionKindResult", result); + } + + @Test + void equalsShouldHandleNullFields() { + TransactionKindResult first = new TransactionKindResult(null, null); + + TransactionKindResult second = new TransactionKindResult(null, null); + + assertEquals(first, second); + } + + @Test + void hashCodeShouldHandleNullValues() { + TransactionKindResult result = new TransactionKindResult(null, null); + + int hashcode = result.hashCode(); + assertNotEquals(0, hashcode); + } + + @Test + void hashCodeShouldHandleNonNullValues() { + TransactionKindResult result = new TransactionKindResult("id1", TransactionKind.RESOLUTION); + + int hashcode = result.hashCode(); + assertNotEquals(0, hashcode); + } +} diff --git a/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializerTest.java b/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializerTest.java index e2fde41..56fab8b 100644 --- a/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializerTest.java +++ b/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateDeserializerTest.java @@ -1,22 +1,24 @@ package uk.gov.companieshouse.api.serialization; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import java.io.IOException; import java.time.LocalDate; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.ObjectMapper; import uk.gov.companieshouse.api.exception.BadRequestException; class LocalDateDeserializerTest { + private static final String DESERIALISATION_FALIED_EXCEPTION_MESSAGE = "Deserialization failed."; + private static final String DATE_FIELD_MISSING_OR_NULL = "$date field is missing or null"; private LocalDateDeserializer deserializer; private ObjectMapper mapper; @@ -29,54 +31,83 @@ void setUp() { } @Test - void dateShouldDeserialize() throws IOException { - + void dateShouldDeserialize() { String jsonTestString = "{\"date\":{\"$date\": \"2023-01-09T00:00:00Z\"}}"; LocalDate returnedDate = deserialize(jsonTestString); - Assertions.assertEquals(LocalDate.of(2023, 1, 9), returnedDate); - + assertEquals(LocalDate.of(2023, 1, 9), returnedDate); } @Test - void longStringReturnsLong() throws IOException { - + void longStringReturnsLong() { String jsonTestString = "{\"date\":{\"$date\": {\"$numberLong\":\"-1431388800000\"}}}"; LocalDate returnedDate = deserialize(jsonTestString); - Assertions.assertEquals(LocalDate.of(1924, 8, 23), returnedDate); - + assertEquals(LocalDate.of(1924, 8, 23), returnedDate); } @Test void nullStringReturnsError() { - String jsonTestString = null; - - assertThrows(NullPointerException.class, ()->{ - deserialize(jsonTestString); - }); + assertThrows(IllegalArgumentException.class, () -> deserialize(jsonTestString)); } @Test void invalidStringReturnsError() { - String jsonTestString = "{\"date\":{\"$date\": \"NotADate\"}}}"; - assertThrows(BadRequestException.class, ()->{ - deserialize(jsonTestString); - }); + assertException(DESERIALISATION_FALIED_EXCEPTION_MESSAGE, jsonTestString); + } + + @Test + void missingDateFieldReturnsError() { + String jsonTestString = "{\"date\":{}}"; + + assertException(DATE_FIELD_MISSING_OR_NULL, jsonTestString); + } + + @Test + void nullDateFieldReturnsError() { + String jsonTestString = "{\"date\":{\"$date\":null}}"; + + assertException(DATE_FIELD_MISSING_OR_NULL, jsonTestString); } - private LocalDate deserialize(String jsonString) throws IOException { - JsonParser parser = mapper.getFactory().createParser(jsonString); - DeserializationContext deserializationContext = mapper.getDeserializationContext(); + @Test + void invalidDateShouldReturnError() { + + JsonParser parser = mock(JsonParser.class); + when(parser.readValueAsTree()) + .thenThrow(new RuntimeException("Invalid JSON")); + BadRequestException exception = assertThrows(BadRequestException.class, () -> deserializer.deserialize(parser, null)); - parser.nextToken(); - parser.nextToken(); - parser.nextToken(); + assertEquals(DESERIALISATION_FALIED_EXCEPTION_MESSAGE, exception.getMessage()); + } + + @Test + void invalidNumberLongReturnError() { + String jsonTestString = "{\"date\":{\"$date\": {\"$numberLong\":\"not-a-number\"}}}"; - return deserializer.deserialize(parser, deserializationContext); + assertException(DESERIALISATION_FALIED_EXCEPTION_MESSAGE, jsonTestString); } - -} + + private LocalDate deserialize(String jsonString) { + try { + JsonParser parser = mapper.createParser(jsonString); + parser.nextToken(); + parser.nextToken(); + parser.nextToken(); + // Pass null for DeserializationContext as it's not used in the deserializer + return deserializer.deserialize(parser, null); + } catch (Exception e) { + throw e; + } + } + + private void assertException(String expectedMessage, String jsonTestString) { + BadRequestException exception = assertThrows(BadRequestException.class, () -> deserialize(jsonTestString)); + + assertEquals(expectedMessage, exception.getMessage()); + } + +} \ No newline at end of file diff --git a/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateSerializerTest.java b/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateSerializerTest.java index 5c31e4d..2f66245 100644 --- a/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateSerializerTest.java +++ b/src/test/java/uk/gov/companieshouse/api/serialization/LocalDateSerializerTest.java @@ -1,6 +1,5 @@ package uk.gov.companieshouse.api.serialization; -import com.fasterxml.jackson.core.JsonGenerator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -9,6 +8,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import tools.jackson.core.JsonGenerator; import java.time.LocalDate; @@ -31,7 +31,7 @@ void setUp() { } @Test - void dateShouldSerialize() throws Exception { + void dateShouldSerialize() { LocalDate date = LocalDate.of(2020, 1, 1); serializer.serialize(date, generator, null); @@ -41,10 +41,8 @@ void dateShouldSerialize() throws Exception { } @Test - void assertNullDateReturnsNull() throws Exception { - + void assertNullDateReturnsNull() { serializer.serialize(null, generator, null); - verify(generator).writeNull(); } -} +} \ No newline at end of file