diff --git a/pom.xml b/pom.xml index e819c7c..e9ffd90 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ it.aboutbits spring-boot-toolbox - 2.5.0 + 2.5.1-RC3 Utility library for Spring Boot projects. jar diff --git a/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java index 36242c9..51d4afe 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java @@ -1,6 +1,7 @@ package it.aboutbits.springboot.toolbox.autoconfiguration.web; import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; +import it.aboutbits.springboot.toolbox.reflection.util.CustomTypeReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -41,7 +42,7 @@ public void setAdditionalTypePackages(String[] additionalTypePackages) { this.relevantTypes = findAllCustomTypes(classScanner); } - @SuppressWarnings("rawtypes") + @SuppressWarnings({"rawtypes", "unchecked"}) public static Set> findAllCustomTypes(ClassScannerUtil.ClassScanner classScanner) { return classScanner.getSubTypesOf(CustomType.class).stream() .filter(item -> @@ -50,6 +51,27 @@ public static Set> findAllCustomTypes(ClassScannerUt && !Modifier.isAbstract(item.getModifiers()) && !item.isAnnotationPresent(DisableCustomTypeConfiguration.class) ) + .filter(item -> { + if (item.isEnum()) { + return true; + } + + try { + var constructor = CustomTypeReflectionUtil.getCustomTypeConstructor((Class>) item); + var wrappedType = constructor.getParameterTypes()[0]; + + if (!CustomTypeReflectionUtil.isSupportedWrappedType(wrappedType)) { + log.debug("CustomType {} has an unsupported wrapped type {} and will be ignored.", item.getName(), wrappedType.getName()); + return false; + } + + return true; + } catch (NoSuchMethodException _) { + log.debug("CustomType {} is missing the required constructor and will be ignored.", item.getName()); + + return false; + } + }) .collect(Collectors.toSet()); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java b/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java index 877447e..ccbc825 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java @@ -23,24 +23,29 @@ @NullMarked public class CustomTypeDeserializer> extends ValueDeserializer { private final Class customType; - private final Constructor constructor; + private final @Nullable Constructor constructor; private final Function typeConverter; public CustomTypeDeserializer(Class customType) { this.customType = customType; - try { - this.constructor = CustomTypeReflectionUtil.getCustomTypeConstructor(customType); - } catch (NoSuchMethodException e) { - throw new CustomTypeDeserializerException( - "Unable to find constructor for type: " + customType.getName(), - e + if (customType.isEnum()) { + this.constructor = null; + this.typeConverter = getEnumConverter(customType); + } else { + try { + this.constructor = CustomTypeReflectionUtil.getCustomTypeConstructor(customType); + } catch (NoSuchMethodException e) { + throw new CustomTypeDeserializerException( + "Unable to find constructor for type: " + customType.getName(), + e + ); + } + + this.typeConverter = getTypeConverter( + constructor.getParameterTypes()[0] ); } - - this.typeConverter = getTypeConverter( - constructor.getParameterTypes()[0] - ); } @Override @@ -49,9 +54,18 @@ public Class handledType() { } @Override - public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) { + @SuppressWarnings("unchecked") + public @Nullable T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) { var value = typeConverter.apply(jsonParser); + if (value == null) { + return null; + } + + if (constructor == null) { + return (T) value; + } + try { return constructor.newInstance(value); } catch ( diff --git a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/CustomTypeReflectionUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/CustomTypeReflectionUtil.java index 3d39ee0..9689c0d 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/CustomTypeReflectionUtil.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/CustomTypeReflectionUtil.java @@ -1,11 +1,15 @@ package it.aboutbits.springboot.toolbox.reflection.util; import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import org.jspecify.annotations.NullMarked; import java.lang.reflect.Constructor; import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Arrays; +import java.util.UUID; @NullMarked public final class CustomTypeReflectionUtil { @@ -17,7 +21,7 @@ public static > Constructor getCustomTypeConstructor( return customType.getConstructor( getWrappedType(customType) ); - } catch (NoSuchMethodException | SecurityException e) { + } catch (NoSuchMethodException | SecurityException _) { throw new NoSuchMethodException(); } } @@ -43,4 +47,22 @@ public static Class getWrappedType(Class> customType) throw new NoSuchMethodException(); } + + public static boolean isSupportedWrappedType(Class wrappedType) { + return Boolean.class.isAssignableFrom(wrappedType) + || String.class.isAssignableFrom(wrappedType) + || Character.class.isAssignableFrom(wrappedType) + || Byte.class.isAssignableFrom(wrappedType) + || Short.class.isAssignableFrom(wrappedType) + || Integer.class.isAssignableFrom(wrappedType) + || Long.class.isAssignableFrom(wrappedType) + || Float.class.isAssignableFrom(wrappedType) + || Double.class.isAssignableFrom(wrappedType) + || BigInteger.class.isAssignableFrom(wrappedType) + || BigDecimal.class.isAssignableFrom(wrappedType) + || ScaledBigDecimal.class.isAssignableFrom(wrappedType) + || UUID.class.isAssignableFrom(wrappedType) + || Enum.class.isAssignableFrom(wrappedType) + || wrappedType.isEnum(); + } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/web/CustomTypePropertyEditor.java b/src/main/java/it/aboutbits/springboot/toolbox/web/CustomTypePropertyEditor.java index 45d9f5d..8ab6afd 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/web/CustomTypePropertyEditor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/web/CustomTypePropertyEditor.java @@ -17,22 +17,27 @@ @NullMarked public final class CustomTypePropertyEditor> extends PropertyEditorSupport { - private final Constructor constructor; + private final @Nullable Constructor constructor; private final Function<@Nullable String, @Nullable Object> typeConverter; public CustomTypePropertyEditor(Class customType) { - try { - this.constructor = CustomTypeReflectionUtil.getCustomTypeConstructor(customType); - } catch (NoSuchMethodException e) { - throw new CustomTypeDeserializer.CustomTypeDeserializerException( - "Unable to find constructor for type: " + customType.getName(), - e + if (customType.isEnum()) { + this.constructor = null; + this.typeConverter = toEnumConverter(customType); + } else { + try { + this.constructor = CustomTypeReflectionUtil.getCustomTypeConstructor(customType); + } catch (NoSuchMethodException e) { + throw new CustomTypeDeserializer.CustomTypeDeserializerException( + "Unable to find constructor for type: " + customType.getName(), + e + ); + } + + this.typeConverter = getTextToTypeConverter( + constructor.getParameters()[0].getType() ); } - - this.typeConverter = getTextToTypeConverter( - constructor.getParameters()[0].getType() - ); } @SuppressWarnings("unchecked") @@ -45,9 +50,14 @@ public String getAsText() { } @Override - public void setAsText(String text) throws IllegalArgumentException { + public void setAsText(@Nullable String text) throws IllegalArgumentException { var value = typeConverter.apply(text); + if (constructor == null) { + setValue(value); + return; + } + try { setValue( constructor.newInstance(value) diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java index 9af44d6..7e9f677 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java @@ -6,6 +6,7 @@ import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithEnumEntityId; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeEnumTestModel; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.DirectEnumEntityId; import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -117,6 +118,50 @@ void emailAddressAsBody() throws Exception { } } + @Nested + @ArchIgnoreNoProductionCounterpart + class DirectEnumEntityIdTest { + @Test + void valueAsPathVariable() throws Exception { + var value = DirectEnumEntityId.VAL1; + + var resultAsString = performGetAndReturnResult( + String.format("/test/entity-id/DirectEnumEntityId/as-path-variable/%s", value) + ); + + var actual = jsonMapper.readValue(resultAsString, DirectEnumEntityId.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void valueAsRequestParameter() throws Exception { + var value = DirectEnumEntityId.VAL2; + + var resultAsString = performGetAndReturnResult( + String.format("/test/entity-id/DirectEnumEntityId/as-request-parameter?value=%s", value) + ); + + var actual = jsonMapper.readValue(resultAsString, DirectEnumEntityId.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void valueAsBody() throws Exception { + var value = DirectEnumEntityId.VAL1; + + var resultAsString = performPostAndReturnResult( + "/test/entity-id/DirectEnumEntityId/as-body", + value + ); + + var actual = jsonMapper.readValue(resultAsString, DirectEnumEntityId.class); + + assertThat(actual).isEqualTo(value); + } + } + private String performGetAndReturnResult(String url) throws Exception { var requestBuilder = MockMvcRequestBuilders.get(url) .contentType(MediaType.APPLICATION_JSON); diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java index 38d87de..a46c6e2 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java @@ -4,6 +4,7 @@ import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithEnumEntityId; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeEnumTestModel; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.DirectEnumEntityId; import org.jspecify.annotations.NullMarked; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -46,4 +47,19 @@ public CustomTypeEnumTestModel.ID customTypeEnumTestModelIdAsRequestParameter(@R public BodyWithEnumEntityId customTypeEnumTestModelIdAsBody(@RequestBody BodyWithEnumEntityId value) { return value; } + + @GetMapping("/DirectEnumEntityId/as-path-variable/{value}") + public DirectEnumEntityId directEnumEntityIdAsPathVariable(@PathVariable DirectEnumEntityId value) { + return value; + } + + @GetMapping("/DirectEnumEntityId/as-request-parameter") + public DirectEnumEntityId directEnumEntityIdAsRequestParameter(@RequestParam DirectEnumEntityId value) { + return value; + } + + @PostMapping("/DirectEnumEntityId/as-body") + public DirectEnumEntityId directEnumEntityIdAsBody(@RequestBody DirectEnumEntityId value) { + return value; + } } diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java index cc51eaf..4b79dfb 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java @@ -7,6 +7,9 @@ import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeEnumTestModelRepository; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModelRepository; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.DirectEnumEntityId; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.DirectEnumIdTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.DirectEnumIdTestModelRepository; import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.ReferencedTestModel; import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Nested; @@ -28,10 +31,13 @@ class EntityIdJpaTest { @Autowired CustomTypeEnumTestModelRepository repositoryEnum; + @Autowired + DirectEnumIdTestModelRepository repositoryDirectEnum; + @Nested class OwnId { @Test - void inAndOut_shouldSucceed() { + void ownId_inAndOut_shouldSucceed() { var item = new CustomTypeTestModel(); var savedItem = repository.save(item); @@ -48,7 +54,7 @@ void inAndOut_shouldSucceed() { @Nested class ReferencedId { @Test - void inAndOut_shouldSucceed() { + void referencedId_inAndOut_shouldSucceed() { var item = new CustomTypeTestModel(); item.setReferencedId(new ReferencedTestModel.ID(1234L)); @@ -66,7 +72,7 @@ void inAndOut_shouldSucceed() { @Nested class EnumId { @Test - void inAndOut_shouldSucceed() { + void enumId_inAndOut_shouldSucceed() { var values = CustomTypeEnumTestModel.CustomTypeEnum.values(); var item = new CustomTypeEnumTestModel(); @@ -84,4 +90,24 @@ values[new Random().nextInt(values.length)] .isEqualTo(savedItem); } } + + @Nested + class DirectEnumId { + @Test + void directEnumId_inAndOut_shouldSucceed() { + var values = DirectEnumEntityId.values(); + + var item = new DirectEnumIdTestModel(); + item.setId(values[new Random().nextInt(values.length)]); + + var savedItem = repositoryDirectEnum.save(item); + + var retrievedItem = repositoryDirectEnum.findById(savedItem.getId()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } } diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumEntityId.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumEntityId.java new file mode 100644 index 0000000..8a9f7af --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumEntityId.java @@ -0,0 +1,15 @@ +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa; + +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public enum DirectEnumEntityId implements EntityId { + VAL1, + VAL2; + + @Override + public DirectEnumEntityId value() { + return this; + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumIdTestModel.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumIdTestModel.java new file mode 100644 index 0000000..ccf0abe --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumIdTestModel.java @@ -0,0 +1,23 @@ +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa; + +import it.aboutbits.springboot.toolbox.type.identity.Identified; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullUnmarked; + +@Entity +@Getter +@Setter +@Table(name = "direct_enum_id_test_model") +@NullUnmarked +public class DirectEnumIdTestModel implements Identified<@NonNull DirectEnumEntityId> { + @Id + @Enumerated(EnumType.STRING) + private DirectEnumEntityId id; +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumIdTestModelRepository.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumIdTestModelRepository.java new file mode 100644 index 0000000..79893a2 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/DirectEnumIdTestModelRepository.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa; + +import org.jspecify.annotations.NullMarked; +import org.springframework.data.jpa.repository.JpaRepository; + +@NullMarked +public interface DirectEnumIdTestModelRepository extends JpaRepository { +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScannerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScannerTest.java new file mode 100644 index 0000000..bfc0200 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScannerTest.java @@ -0,0 +1,67 @@ +package it.aboutbits.springboot.toolbox.autoconfiguration.web; + +import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import org.jspecify.annotations.NullMarked; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@NullMarked +class CustomTypeScannerTest { + @Test + @SuppressWarnings("unchecked") + void findAllCustomTypes_shouldFilterOutTypesMissingConstructor() { + // given + ClassScannerUtil.ClassScanner classScanner = mock(ClassScannerUtil.ClassScanner.class); + when(classScanner.getSubTypesOf(CustomType.class)).thenReturn(Set.of( + EnumCustomType.class, + InvalidCustomType.class, + UnsupportedWrappedTypeCustomType.class + )); + + // when + Set> result = CustomTypeScanner.findAllCustomTypes(classScanner); + + // then + assertThat(result) + .containsExactly(EnumCustomType.class) + .doesNotContain(InvalidCustomType.class) + .doesNotContain(UnsupportedWrappedTypeCustomType.class); + } + + public enum EnumCustomType implements CustomType { + VALUE; + + @Override + public EnumCustomType value() { + return this; + } + } + + @SuppressWarnings("checkstyle:RedundantModifier") + public static class InvalidCustomType implements CustomType { + @Override + public String value() { + return "test"; + } + } + + @SuppressWarnings("checkstyle:RedundantModifier") + public static class UnsupportedWrappedTypeCustomType implements CustomType { + private final Object value; + + public UnsupportedWrappedTypeCustomType(Object value) { + this.value = value; + } + + @Override + public Object value() { + return value; + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializerTest.java new file mode 100644 index 0000000..2b96173 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializerTest.java @@ -0,0 +1,44 @@ +package it.aboutbits.springboot.toolbox.jackson; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import org.jspecify.annotations.NullMarked; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +@NullMarked +class CustomTypeDeserializerTest { + @Test + void constructor_withValidCustomType_shouldNotThrow() { + assertThatCode(() -> new CustomTypeDeserializer<>(EnumCustomType.class)) + .doesNotThrowAnyException(); + } + + @Test + void constructor_withInvalidCustomType_shouldThrowException() { + assertThatExceptionOfType(CustomTypeDeserializer.CustomTypeDeserializerException.class) + .isThrownBy(() -> new CustomTypeDeserializer<>(InvalidCustomType.class)) + .withMessageContaining( + "Unable to find constructor for type: it.aboutbits.springboot.toolbox.jackson.CustomTypeDeserializerTest$InvalidCustomType" + ); + } + + public enum EnumCustomType implements CustomType { + VALUE; + + @Override + public EnumCustomType value() { + return this; + } + } + + @SuppressWarnings("checkstyle:RedundantModifier") + public static class InvalidCustomType implements CustomType { + @Override + public String value() { + return "test"; + } + // Missing constructor(String) + } +} diff --git a/src/test/resources/db/changelog/2026-05-06-create-direct-enum-id-test-model-table.yml b/src/test/resources/db/changelog/2026-05-06-create-direct-enum-id-test-model-table.yml new file mode 100644 index 0000000..f6e21ab --- /dev/null +++ b/src/test/resources/db/changelog/2026-05-06-create-direct-enum-id-test-model-table.yml @@ -0,0 +1,14 @@ +databaseChangeLog: + - changeSet: + author: Thomas Sapelza + id: 2026-05-06-create-direct-enum-id-test-model-table + changes: + - createTable: + tableName: direct_enum_id_test_model + columns: + - column: + name: id + type: text + constraints: + nullable: false + primaryKey: true diff --git a/src/test/resources/db/changelog/master.yml b/src/test/resources/db/changelog/master.yml index cdfe892..83bb9ea 100644 --- a/src/test/resources/db/changelog/master.yml +++ b/src/test/resources/db/changelog/master.yml @@ -11,3 +11,6 @@ databaseChangeLog: - include: file: 2025-08-29-create-custom-type-enum-testing-table.yml relativeToChangelogFile: true + - include: + file: 2026-05-06-create-direct-enum-id-test-model-table.yml + relativeToChangelogFile: true