diff --git a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/Naksha.kt b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/Naksha.kt index ac727e3de..a4fd7a0ca 100644 --- a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/Naksha.kt +++ b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/Naksha.kt @@ -77,6 +77,11 @@ class Naksha private constructor() { @JsStatic val VIRT_DICTIONARIES_QUOTED = quoteIdent(VIRT_DICTIONARIES) + /** + * Maximum collectionId name length allowed to give by clients. Rest of "free" characters are reserved for partitioning suffix. + */ + private const val MAX_COLLECTION_ID_NAME_LENGTH = 44 + /** * Tests if the given **id** is a valid identifier, so matches: * @@ -89,7 +94,7 @@ class Naksha private constructor() { @JsStatic @JvmStatic fun isValidId(id: String?): Boolean { - if (id.isNullOrEmpty() || "naksha" == id || id.length > 32) return false + if (id.isNullOrEmpty() || "naksha" == id || id.length > MAX_COLLECTION_ID_NAME_LENGTH) return false var i = 0 var c = id[i++] // First character must be a-z @@ -113,8 +118,12 @@ class Naksha private constructor() { @JsStatic @JvmStatic fun verifyId(id: String?) { - if (id.isNullOrEmpty() || "naksha" == id || id.length > 32) { - throw NakshaException(ILLEGAL_ID, "The given identifier is null, empty or has more than 32 characters", id = id) + if (id.isNullOrEmpty() || "naksha" == id || id.length > MAX_COLLECTION_ID_NAME_LENGTH) { + throw NakshaException( + ILLEGAL_ID, + "The given identifier is null, empty or has more than $MAX_COLLECTION_ID_NAME_LENGTH characters", + id = id + ) } var i = 0 var c = id[i++] @@ -170,10 +179,18 @@ class Naksha private constructor() { for (part in parts) { for (c in part) { when (c) { - in 'a'..'z', in 'A'..'Z', in '0'..'9','_' -> sb.append(c) - '"' -> { quoted = true; sb.append('"').append('"') } - '\\' -> { quoted = true; sb.append('\\').append('\\') } - else -> { quoted = true; sb.append(c) } + in 'a'..'z', in 'A'..'Z', in '0'..'9', '_' -> sb.append(c) + '"' -> { + quoted = true; sb.append('"').append('"') + } + + '\\' -> { + quoted = true; sb.append('\\').append('\\') + } + + else -> { + quoted = true; sb.append(c) + } } } } diff --git a/here-naksha-lib-model/src/commonTest/kotlin/naksha/model/NakshaTest.kt b/here-naksha-lib-model/src/commonTest/kotlin/naksha/model/NakshaTest.kt new file mode 100644 index 000000000..7ccedd945 --- /dev/null +++ b/here-naksha-lib-model/src/commonTest/kotlin/naksha/model/NakshaTest.kt @@ -0,0 +1,47 @@ +package naksha.model + +import naksha.base.PlatformUtil.PlatformUtilCompanion.randomString +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class NakshaTest { + + @Test + fun shouldLimitCollectionIdLength() { + // expect + var collectionId = collectionIdOf(1) + assertTrue(collectionId) { Naksha.isValidId(collectionId) } + collectionId = collectionIdOf(44) + assertTrue(collectionId) { Naksha.isValidId(collectionId) } + + collectionId = collectionIdOf(45) + assertFalse(collectionId) { Naksha.isValidId(collectionId) } + assertFalse(collectionId) { Naksha.isValidId("") } + } + + @Test + fun shouldOnlyAllowCharacterAsFirstChar() { + // expect + assertTrue{ Naksha.isValidId("c1232_name") } + assertFalse{ Naksha.isValidId("11232_name") } + } + + @Test + fun shouldNotAllowCapitalLettersOrUnsupportedCharacters() { + // expect + assertFalse{ Naksha.isValidId("C1232_name") } + assertFalse{ Naksha.isValidId("name\$a") } + assertFalse{ Naksha.isValidId("name&a") } + assertFalse{ Naksha.isValidId("name*a") } + assertFalse{ Naksha.isValidId("name#a") } + assertFalse{ Naksha.isValidId("name@a") } + assertFalse{ Naksha.isValidId("name!a") } + + assertTrue{ Naksha.isValidId("name_a") } + assertTrue{ Naksha.isValidId("name-a") } + assertTrue{ Naksha.isValidId("name:a") } + } + + private fun collectionIdOf(length: Int): String = "c" + randomString(length - 1).lowercase() +} \ No newline at end of file