From fbc9ecb35d239216e21e9d51971407129ec1e05e Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 15 May 2024 11:23:26 +0530 Subject: [PATCH 1/2] Fixes #966 Fixes #966 by introducing a boolean flag in `NitriteConfig` to disable type validation --- CHANGELOG.md | 4 ++ .../java/org/dizitart/no2/NitriteBuilder.java | 23 ++++++++- .../java/org/dizitart/no2/NitriteConfig.java | 22 +++++++- .../no2/common/util/ValidationUtils.java | 22 ++++---- .../no2/repository/RepositoryFactory.java | 7 +-- .../no2/common/util/ValidationUtilsTest.java | 24 +++++---- pom.xml | 7 +++ potassium-nitrite/pom.xml | 5 ++ .../main/kotlin/org/dizitart/kno2/Builder.kt | 24 +++++++++ .../kno2/KotlinXSerializationMapperTest.kt | 50 ++++++++++++++++++- 10 files changed, 160 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b17b1a04..76adcc951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,14 @@ - Nitrite now supports JPMS. It is now modular and can be used in Java 9 or above. - Version upgrade for several dependencies +- Repository type validation can be disabled in `NitriteBuilder` as a fix for #966 ### Issue Fixes - Fix for #935 +- Fix for #948 +- Fix for #961 +- Fix for #966 ## Release 4.2.2 - Mar 5, 2024 diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java index 84625e245..35373d9de 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java @@ -31,8 +31,8 @@ * @see Nitrite * @since 1.0 */ +@Getter public class NitriteBuilder { - @Getter /** * The Nitrite configuration object. */ @@ -61,6 +61,27 @@ public NitriteBuilder fieldSeparator(String separator) { return this; } + /** + * Disables the repository type validation for the Nitrite database. + *

+ * Repository type validation is a feature in Nitrite that ensures the type of the objects + * stored in the repository can be converted to and from {@link org.dizitart.no2.collection.Document}. + *

+ * By default, the repository type validation is enabled. If you disable it, and if you try to + * store an object that cannot be converted to a {@link org.dizitart.no2.collection.Document}, + * then Nitrite will throw an exception during the operation. + * + * @return the NitriteBuilder instance with repository type validation disabled + * @see org.dizitart.no2.collection.Document + * @see org.dizitart.no2.repository.ObjectRepository + * @see org.dizitart.no2.common.mapper.EntityConverter + * @since 4.3.0 + */ + public NitriteBuilder disableRepositoryTypeValidation() { + this.nitriteConfig.disableRepositoryTypeValidation(); + return this; + } + /** * Registers an {@link EntityConverter} with the Nitrite database. * An {@link EntityConverter} is used to convert between an entity and a diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java index ae8f3ac40..0c2d043b9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java @@ -79,6 +79,12 @@ public class NitriteConfig implements AutoCloseable { */ private Integer schemaVersion = Constants.INITIAL_SCHEMA_VERSION; + @Getter + /** + * Indicates if repository type validation is disabled. + */ + private boolean repositoryTypeValidationDisabled = false; + /** * Instantiates a new {@link NitriteConfig}. */ @@ -103,6 +109,20 @@ public void fieldSeparator(String separator) { NitriteConfig.fieldSeparator = separator; } + /** + * Disables repository type validation. + * + * @throws InvalidOperationException if the repository type validation is attempted to be + * changed after database initialization. + */ + public void disableRepositoryTypeValidation() { + if (configured) { + throw new InvalidOperationException("Cannot change repository type validation after database" + + " initialization"); + } + this.repositoryTypeValidationDisabled = true; + } + /** * Registers an {@link EntityConverter} with the Nitrite database. * @@ -157,7 +177,7 @@ public NitriteConfig addMigration(Migration migration) { TreeMap targetMap = migrations.computeIfAbsent(start, k -> new TreeMap<>()); Migration existing = targetMap.get(end); if (existing != null) { - log.warn("Overriding migration " + existing + " with " + migration); + log.warn("Overriding migration {} with {}", existing, migration); } targetMap.put(end, migration); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index 6adfcb860..2c664eb53 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -16,6 +16,7 @@ package org.dizitart.no2.common.util; +import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.IndexingException; @@ -149,7 +150,7 @@ public static void validateProjectionType(Class type, NitriteMapper nitriteMa } } - public static void validateRepositoryType(Class type, NitriteMapper nitriteMapper) { + public static void validateRepositoryType(Class type, NitriteConfig nitriteConfig) { Object value; try { if (type.isInterface() || (Modifier.isAbstract(type.getModifiers()) && !isBuiltInValueType(type))) { @@ -157,14 +158,17 @@ public static void validateRepositoryType(Class type, NitriteMapper nitriteMa return; } - value = newInstance(type, false, nitriteMapper); - if (value == null) { - throw new ValidationException("Cannot create new instance of type " + type); - } - - Document document = (Document) nitriteMapper.tryConvert(value, Document.class); - if (document == null || document.size() == 0) { - throw new ValidationException("Cannot convert to document from type " + type); + if (!nitriteConfig.isRepositoryTypeValidationDisabled()) { + NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); + value = newInstance(type, false, nitriteMapper); + if (value == null) { + throw new ValidationException("Cannot create new instance of type " + type); + } + + Document document = (Document) nitriteMapper.tryConvert(value, Document.class); + if (document == null || document.size() == 0) { + throw new ValidationException("Cannot convert to document from type " + type); + } } } catch (Exception e) { throw new ValidationException("Invalid repository type", e); diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java index 06a7c302f..2d2cf72f5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java @@ -19,7 +19,6 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.CollectionFactory; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; @@ -133,10 +132,9 @@ public void clear() { private ObjectRepository createRepository(NitriteConfig nitriteConfig, Class type, String collectionName, String key) { - NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); NitriteStore store = nitriteConfig.getNitriteStore(); - validateRepositoryType(type, nitriteMapper); + validateRepositoryType(type, nitriteConfig); if (store.getCollectionNames().contains(collectionName)) { throw new ValidationException("A collection with same entity name already exists"); @@ -154,14 +152,13 @@ private ObjectRepository createRepository(NitriteConfig nitriteConfig, Cl private ObjectRepository createRepositoryByDecorator(NitriteConfig nitriteConfig, EntityDecorator entityDecorator, String collectionName, String key) { - NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); NitriteStore store = nitriteConfig.getNitriteStore(); if (store.getCollectionNames().contains(collectionName)) { throw new ValidationException("A collection with same entity name already exists"); } - validateRepositoryType(entityDecorator.getEntityType(), nitriteMapper); + validateRepositoryType(entityDecorator.getEntityType(), nitriteConfig); NitriteCollection nitriteCollection = collectionFactory.getCollection(collectionName, nitriteConfig, false); diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java index 5a8e0b17d..5cc36012e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java @@ -16,6 +16,7 @@ package org.dizitart.no2.common.util; +import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.common.mapper.SimpleNitriteMapper; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.integration.Retry; @@ -96,18 +97,19 @@ public void testValidateProjectionType() { @Test public void testValidateRepositoryType() { - SimpleNitriteMapper documentMapper = new SimpleNitriteMapper(); - documentMapper.registerEntityConverter(new ClassA.ClassAConverter()); - documentMapper.registerEntityConverter(new ClassBConverter()); - documentMapper.registerEntityConverter(new EmptyClass.Converter()); + NitriteConfig nitriteConfig = new NitriteConfig(); + nitriteConfig.registerEntityConverter(new ClassA.ClassAConverter()); + nitriteConfig.registerEntityConverter(new ClassBConverter()); + nitriteConfig.registerEntityConverter(new EmptyClass.Converter()); + nitriteConfig.autoConfigure(); - validateRepositoryType(ClassA.class, documentMapper); + validateRepositoryType(ClassA.class, nitriteConfig); - assertThrows(ValidationException.class, () -> validateRepositoryType(EmptyClass.class, documentMapper)); - assertThrows(ValidationException.class, () -> validateRepositoryType(ClassC.class, documentMapper)); - assertThrows(ValidationException.class, () -> validateRepositoryType(String.class, documentMapper)); - assertThrows(ValidationException.class, () -> validateRepositoryType(Number.class, documentMapper)); - assertThrows(ValidationException.class, () -> validateRepositoryType(Integer.class, documentMapper)); - assertThrows(ValidationException.class, () -> validateRepositoryType(Object.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateRepositoryType(EmptyClass.class, nitriteConfig)); + assertThrows(ValidationException.class, () -> validateRepositoryType(ClassC.class, nitriteConfig)); + assertThrows(ValidationException.class, () -> validateRepositoryType(String.class, nitriteConfig)); + assertThrows(ValidationException.class, () -> validateRepositoryType(Number.class, nitriteConfig)); + assertThrows(ValidationException.class, () -> validateRepositoryType(Integer.class, nitriteConfig)); + assertThrows(ValidationException.class, () -> validateRepositoryType(Object.class, nitriteConfig)); } } diff --git a/pom.xml b/pom.xml index d27cc53f5..a17920a9b 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,7 @@ 2.16.1 1.9.24 1.6.3 + 0.6.0-RC.2 1.18.32 1.18.20.0 4.13.2 @@ -291,6 +292,12 @@ ${kotlin.version} test + + org.jetbrains.kotlinx + kotlinx-datetime-jvm + ${kotlinx-datetime.version} + test + diff --git a/potassium-nitrite/pom.xml b/potassium-nitrite/pom.xml index f8468cfad..cea167c10 100644 --- a/potassium-nitrite/pom.xml +++ b/potassium-nitrite/pom.xml @@ -123,6 +123,11 @@ log4j-core test + + org.jetbrains.kotlinx + kotlinx-datetime-jvm + test + diff --git a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt index eb14b6a4d..5661d14ad 100644 --- a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt +++ b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt @@ -37,6 +37,7 @@ class Builder internal constructor() { private val modules = mutableSetOf() private val entityConverters = mutableSetOf>() private val migrations = mutableSetOf() + private var repositoryTypeValidationDisabled: Boolean = false /** * Sets the schema version for the Nitrite database. @@ -49,6 +50,25 @@ class Builder internal constructor() { */ var fieldSeparator: String = NitriteConfig.getFieldSeparator() + /** + * Disables the repository type validation for the Nitrite database. + *

+ * Repository type validation is a feature in Nitrite that ensures the type of the objects + * stored in the repository can be converted to and from [org.dizitart.no2.collection.Document]. + *

+ * By default, the repository type validation is enabled. If you disable it, and if you try to + * store an object that cannot be converted to a [org.dizitart.no2.collection.Document], + * then Nitrite will throw an exception during the operation. + * + * @see org.dizitart.no2.collection.Document + * @see org.dizitart.no2.repository.ObjectRepository + * @see org.dizitart.no2.common.mapper.EntityConverter + * @since 4.3.0 + */ + fun disableRepositoryTypeValidation() { + repositoryTypeValidationDisabled = true + } + /** * Loads a [NitriteModule] into the Nitrite database. The module can be used to extend the * functionality of Nitrite. @@ -88,6 +108,10 @@ class Builder internal constructor() { builder.schemaVersion(schemaVersion) } + if (repositoryTypeValidationDisabled) { + builder.disableRepositoryTypeValidation() + } + if (entityConverters.isNotEmpty()) { entityConverters.forEach { builder.registerEntityConverter(it) } } diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt index 57c8b3678..f84fd5c57 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt @@ -23,6 +23,7 @@ import org.dizitart.kno2.filters.eq import org.dizitart.kno2.serialization.KotlinXSerializationMapper import org.dizitart.no2.collection.Document import org.dizitart.no2.common.module.NitriteModule.module +import org.dizitart.no2.exceptions.ValidationException import org.dizitart.no2.mvstore.MVStoreModule import org.junit.Test import org.slf4j.LoggerFactory @@ -180,4 +181,51 @@ class KotlinXSerializationMapperTest { testData.someArray.forEachIndexed { index, s -> assertEquals(decodedObject.someArray[index], s) } assertEquals(testData, decodedObject.copy(someArray = testData.someArray)) } -} \ No newline at end of file + + @Test(expected = ValidationException::class) + fun testRepositoryValidationEnabled() { + val db = nitrite { + loadModule(MVStoreModule(dbPath)) + loadModule(module(KotlinXSerializationMapper())) + } + + val repo = db.getRepository() + repo.insert(CacheEntry("sha256", kotlinx.datetime.Clock.System.now())) + repo.find(CacheEntry::sha256 eq "sha256").firstOrNull().also { + assertEquals(it?.sha256, "sha256") + } + db.close() + try { + Files.delete(Paths.get(dbPath)) + } catch (e: Exception) { + log.error("Failed to delete db file", e) + } + } + + @Test + fun testRepositoryValidationDisabled() { + val db = nitrite { + disableRepositoryTypeValidation() + loadModule(MVStoreModule(dbPath)) + loadModule(module(KotlinXSerializationMapper())) + } + + val repo = db.getRepository() + repo.insert(CacheEntry("sha256", kotlinx.datetime.Clock.System.now())) + repo.find(CacheEntry::sha256 eq "sha256").firstOrNull().also { + assertEquals(it?.sha256, "sha256") + } + db.close() + try { + Files.delete(Paths.get(dbPath)) + } catch (e: Exception) { + log.error("Failed to delete db file", e) + } + } +} + +@Serializable +data class CacheEntry( + val sha256: String, + val lastUpdated: kotlinx.datetime.Instant +) \ No newline at end of file From 9807a0deafdee754263b66f59eee45ac8537a65c Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 15 May 2024 22:18:10 +0530 Subject: [PATCH 2/2] introduced a new property instead of function --- .../src/main/kotlin/org/dizitart/kno2/Builder.kt | 9 +++------ .../org/dizitart/kno2/KotlinXSerializationMapperTest.kt | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt index 5661d14ad..21b8cd418 100644 --- a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt +++ b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt @@ -37,7 +37,6 @@ class Builder internal constructor() { private val modules = mutableSetOf() private val entityConverters = mutableSetOf>() private val migrations = mutableSetOf() - private var repositoryTypeValidationDisabled: Boolean = false /** * Sets the schema version for the Nitrite database. @@ -51,7 +50,7 @@ class Builder internal constructor() { var fieldSeparator: String = NitriteConfig.getFieldSeparator() /** - * Disables the repository type validation for the Nitrite database. + * Enables/disables the repository type validation for the Nitrite database. *

* Repository type validation is a feature in Nitrite that ensures the type of the objects * stored in the repository can be converted to and from [org.dizitart.no2.collection.Document]. @@ -65,9 +64,7 @@ class Builder internal constructor() { * @see org.dizitart.no2.common.mapper.EntityConverter * @since 4.3.0 */ - fun disableRepositoryTypeValidation() { - repositoryTypeValidationDisabled = true - } + var enableRepositoryValidation = true /** * Loads a [NitriteModule] into the Nitrite database. The module can be used to extend the @@ -108,7 +105,7 @@ class Builder internal constructor() { builder.schemaVersion(schemaVersion) } - if (repositoryTypeValidationDisabled) { + if (!enableRepositoryValidation) { builder.disableRepositoryTypeValidation() } diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt index f84fd5c57..b098fbe9d 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/KotlinXSerializationMapperTest.kt @@ -205,7 +205,7 @@ class KotlinXSerializationMapperTest { @Test fun testRepositoryValidationDisabled() { val db = nitrite { - disableRepositoryTypeValidation() + enableRepositoryValidation = false loadModule(MVStoreModule(dbPath)) loadModule(module(KotlinXSerializationMapper())) }