Private
Public Access
1
0

[android] backend: normalize base url

This commit is contained in:
2026-04-12 11:26:38 -07:00
parent fd3660858e
commit 7056a7f836
5 changed files with 32 additions and 7 deletions

View File

@@ -5,7 +5,7 @@
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

1
android/.idea/vcs.xml generated
View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -24,7 +24,7 @@ data class ServerConfig(
fun loadFromSettings(context: Context): ServerConfig {
val prefs = getSharedPreferences(context)
return ServerConfig(
serverName = prefs.getString("serverName", null),
serverName = prefs.getString("serverName", null).normalizedBaseUrl(),
authentication = ServerAuthentication.loadFromEncryptedSettings(context)
)
}
@@ -37,7 +37,7 @@ data class ServerConfig(
fun saveToSettings(context: Context) {
val prefs = getSharedPreferences(context)
prefs.edit {
putString("serverName", serverName)
putString("serverName", serverName.normalizedBaseUrl())
apply()
}
@@ -45,6 +45,11 @@ data class ServerConfig(
}
}
fun String?.normalizedBaseUrl(): String? {
val value = this?.trim()?.takeIf { it.isNotEmpty() } ?: return null
return if (value.endsWith("/")) value else "$value/"
}
data class ServerAuthentication(
val username: String,
val password: String,
@@ -101,7 +106,9 @@ class ServerConfigRepository @Inject constructor(
fun applyConfig(applicator: ServerConfig.() -> Unit) {
val config = _serverConfig.value.copy()
_serverConfig.value = config.apply(applicator)
_serverConfig.value = config.apply(applicator).also {
it.serverName = it.serverName.normalizedBaseUrl()
}
_serverConfig.value.saveToSettings(context)
}
}

View File

@@ -104,8 +104,10 @@ class APIClientFactory {
return InvalidConfigurationAPIClient(InvalidConfigurationAPIClient.Issue.NOT_CONFIGURED)
}
val normalizedServerString = serverString.ensureTrailingSlash()
// Try to parse server string
val serverURL = HttpUrl.parse(serverString)
val serverURL = HttpUrl.parse(normalizedServerString)
?: return InvalidConfigurationAPIClient(InvalidConfigurationAPIClient.Issue.INVALID_HOST_URL)
return RetrofitAPIClient(serverURL.url(), authentication)
@@ -113,6 +115,10 @@ class APIClientFactory {
}
}
private fun String.ensureTrailingSlash(): String {
return if (endsWith("/")) this else "$this/"
}
// TODO: Is this a dumb idea?
class InvalidConfigurationAPIClient(val issue: Issue): APIClient {
enum class Issue {
@@ -215,4 +221,4 @@ fun URL.authenticatedWebSocketURL(serverPath: String, params: Map<String, String
}
return URL(requestURL.build().toString())
}
}

View File

@@ -11,6 +11,7 @@ import net.buzzert.kordophone.backend.db.CachedChatDatabase
import net.buzzert.kordophone.backend.model.Message
import net.buzzert.kordophone.backend.model.OutgoingMessage
import net.buzzert.kordophone.backend.server.APIClient
import net.buzzert.kordophone.backend.server.APIClientFactory
import net.buzzert.kordophone.backend.server.APIInterface
import net.buzzert.kordophone.backend.server.Authentication
import net.buzzert.kordophone.backend.server.ChatRepository
@@ -38,6 +39,16 @@ class BackendTests {
return Pair(repository, mockServer)
}
@Test
fun testCreateClientAcceptsBaseUrlWithoutTrailingSlash() {
val client = APIClientFactory.createClient(
"https://example.com/api",
Authentication("test", "test")
)
assertTrue(client.isConfigured)
}
@Test
fun testGetVersion() = runBlocking {
val (repository, mockServer) = mockRepository()
@@ -342,4 +353,4 @@ class BackendTests {
assertEquals(messagesToGenerate, allMessages.count())
}
}
}
}