diff --git a/Studio.kt b/Studio.kt index 9783d0e4b..ed8c19079 100644 --- a/Studio.kt +++ b/Studio.kt @@ -86,6 +86,7 @@ import com.vaticle.typedb.studio.module.project.ProjectDialog import com.vaticle.typedb.studio.module.type.TypeBrowser import com.vaticle.typedb.studio.module.type.TypeDialog import com.vaticle.typedb.studio.module.type.TypeEditor +import com.vaticle.typedb.studio.module.user.UpdateDefaultPasswordDialog import com.vaticle.typedb.studio.service.Service import com.vaticle.typedb.studio.service.common.util.Label import com.vaticle.typedb.studio.service.common.util.Message @@ -158,6 +159,7 @@ object Studio { PreferenceDialog.MayShowDialogs() ProjectDialog.MayShowDialogs(window) TypeDialog.MayShowDialogs() + UpdateDefaultPasswordDialog.MayShowDialogs() } } } diff --git a/framework/common/Util.kt b/framework/common/Util.kt index 60f30a563..bc2b480e0 100644 --- a/framework/common/Util.kt +++ b/framework/common/Util.kt @@ -34,6 +34,8 @@ import kotlin.math.roundToInt object Util { + const val ELLIPSES = "..." + fun Rect.contains(x: Int, y: Int): Boolean = this.contains(Offset(x.toFloat(), y.toFloat())) fun toRectDP(rawRectangle: Rect, density: Float) = Rect( @@ -73,6 +75,10 @@ object Util { pop() }.toAnnotatedString() + fun mayTruncate(string: String, length: Int): String { + return if (string.length <= length) string else string.take(length) + ELLIPSES + } + fun String.hyphenate(): String = this.replace(" ", "-") // TODO: Investigate usages of this method -- why were they needed to begin with. Most likely is race condition. diff --git a/framework/output/LogOutput.kt b/framework/output/LogOutput.kt index b945ed678..76b3a9e9c 100644 --- a/framework/output/LogOutput.kt +++ b/framework/output/LogOutput.kt @@ -36,6 +36,7 @@ import com.vaticle.typedb.driver.api.concept.thing.Relation import com.vaticle.typedb.driver.api.concept.thing.Thing import com.vaticle.typedb.driver.api.concept.type.Type import com.vaticle.typedb.driver.api.concept.value.Value +import com.vaticle.typedb.studio.framework.common.Util import com.vaticle.typedb.studio.framework.common.theme.Color import com.vaticle.typedb.studio.framework.common.theme.Theme import com.vaticle.typedb.studio.framework.editor.TextEditor @@ -134,7 +135,7 @@ internal class LogOutput constructor( if (!isCollecting.get()) return@launchAndHandle val timeSinceLastResponse = System.currentTimeMillis() - lastOutputTime.get() if (timeSinceLastResponse >= RUNNING_INDICATOR_DELAY.inWholeMilliseconds) { - output(INFO, "...") + output(INFO, Util.ELLIPSES) duration = RUNNING_INDICATOR_DELAY } else { duration = RUNNING_INDICATOR_DELAY - timeSinceLastResponse.milliseconds diff --git a/module/Toolbar.kt b/module/Toolbar.kt index 479975f69..b333715dd 100644 --- a/module/Toolbar.kt +++ b/module/Toolbar.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.graphics.Color import com.vaticle.typedb.driver.api.TypeDBSession import com.vaticle.typedb.driver.api.TypeDBTransaction import com.vaticle.typedb.studio.framework.common.URL +import com.vaticle.typedb.studio.framework.common.Util.mayTruncate import com.vaticle.typedb.studio.framework.common.theme.Theme import com.vaticle.typedb.studio.framework.common.theme.Theme.TOOLBAR_BUTTON_SIZE import com.vaticle.typedb.studio.framework.common.theme.Theme.TOOLBAR_SEPARATOR_HEIGHT @@ -61,6 +62,8 @@ import com.vaticle.typedb.studio.service.connection.DriverState.Status.DISCONNEC object Toolbar { + private const val CONNECTION_NAME_LENGTH_LIMIT = 50 + private val isConnected get() = Service.driver.isConnected private val isScript get() = Service.driver.isScriptMode private val isInteractive get() = Service.driver.isInteractiveMode @@ -524,7 +527,7 @@ object Toolbar { when (Service.driver.status) { DISCONNECTED -> ConnectionButton(Label.CONNECT_TO_TYPEDB) CONNECTING -> ConnectionButton(Label.CONNECTING) - CONNECTED -> ConnectionButton(Service.driver.connectionName!!) + CONNECTED -> ConnectionButton(mayTruncate(Service.driver.connectionName!!, CONNECTION_NAME_LENGTH_LIMIT)) } } diff --git a/module/connection/ServerDialog.kt b/module/connection/ServerDialog.kt index ddcbab85e..a485700bd 100644 --- a/module/connection/ServerDialog.kt +++ b/module/connection/ServerDialog.kt @@ -58,7 +58,7 @@ import com.vaticle.typedb.studio.framework.material.Tooltip import com.vaticle.typedb.studio.service.Service import com.vaticle.typedb.studio.service.common.util.Label import com.vaticle.typedb.studio.service.common.util.Property -import com.vaticle.typedb.studio.service.common.util.Property.Server.TYPEDB +import com.vaticle.typedb.studio.service.common.util.Property.Server.TYPEDB_CORE import com.vaticle.typedb.studio.service.common.util.Property.Server.TYPEDB_ENTERPRISE import com.vaticle.typedb.studio.service.common.util.Sentence import com.vaticle.typedb.studio.service.connection.DriverState.Status.CONNECTED @@ -76,41 +76,32 @@ object ServerDialog { private val state by mutableStateOf(ConnectServerForm()) private class ConnectServerForm : Form.State() { - var server: Property.Server by mutableStateOf(appData.server ?: Property.Server.TYPEDB) + var server: Property.Server by mutableStateOf(appData.server ?: Property.Server.TYPEDB_CORE) var coreAddress: String by mutableStateOf(appData.coreAddress ?: "") var enterpriseAddresses: MutableList = mutableStateListOf().also { appData.enterpriseAddresses?.let { saved -> it.addAll(saved) } } var username: String by mutableStateOf(appData.username ?: "") var password: String by mutableStateOf("") - var tlsEnabled: Boolean by mutableStateOf(appData.tlsEnabled ?: false) + var tlsEnabled: Boolean by mutableStateOf(appData.tlsEnabled ?: true) var caCertificate: String by mutableStateOf(appData.caCertificate ?: "") override fun cancel() = Service.driver.connectServerDialog.close() override fun isValid(): Boolean = when (server) { - TYPEDB -> coreAddress.isNotBlank() && addressFormatIsValid(coreAddress) + TYPEDB_CORE -> coreAddress.isNotBlank() && addressFormatIsValid(coreAddress) TYPEDB_ENTERPRISE -> !(enterpriseAddresses.isEmpty() || username.isBlank() || password.isBlank()) } override fun submit() { when (server) { - TYPEDB -> { - Service.driver.tryConnectToTypeDBAsync(coreAddress) { - Service.driver.connectServerDialog.close() - } - } - TYPEDB_ENTERPRISE -> { - val onSuccess = Service.driver.connectServerDialog::close - when { - caCertificate.isBlank() || !tlsEnabled -> Service.driver.tryConnectToTypeDBEnterpriseAsync( - enterpriseAddresses.toSet(), username, password, tlsEnabled, onSuccess - ) - else -> Service.driver.tryConnectToTypeDBEnterpriseAsync( - enterpriseAddresses.toSet(), username, password, caCertificate, onSuccess - ) - } + TYPEDB_CORE -> Service.driver.tryConnectToTypeDBCoreAsync(coreAddress) { + Service.driver.connectServerDialog.close() } + TYPEDB_ENTERPRISE -> Service.driver.tryConnectToTypeDBEnterpriseAsync( + enterpriseAddresses.toSet(), username, password, tlsEnabled, caCertificate + ) { Service.driver.connectServerDialog.close() } } + password = "" appData.server = server appData.coreAddress = coreAddress appData.enterpriseAddresses = enterpriseAddresses @@ -158,7 +149,7 @@ object ServerDialog { PasswordFormField(state) TLSEnabledFormField(state) if (state.tlsEnabled) CACertificateFormField(state = state, dialogWindow = window) - } else if (state.server == TYPEDB) { + } else if (state.server == TYPEDB_CORE) { CoreAddressFormField(state, shouldFocus = Service.driver.isDisconnected) } Spacer(Modifier.weight(1f)) diff --git a/module/user/UpdateDefaultPasswordDialog.kt b/module/user/UpdateDefaultPasswordDialog.kt new file mode 100644 index 000000000..59c425fa0 --- /dev/null +++ b/module/user/UpdateDefaultPasswordDialog.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 Vaticle + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * + */ + +package com.vaticle.typedb.studio.module.user + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.vaticle.typedb.studio.framework.material.Dialog +import com.vaticle.typedb.studio.framework.material.Form +import com.vaticle.typedb.studio.service.Service +import com.vaticle.typedb.studio.service.common.util.Label +import com.vaticle.typedb.studio.service.common.util.Sentence.UPDATE_DEFAULT_PASSWORD_FOR_USERNAME +import com.vaticle.typedb.studio.service.common.util.Sentence.UPDATE_DEFAULT_PASSWORD_INSTRUCTION + +object UpdateDefaultPasswordDialog { + + private val WIDTH = 500.dp + private val HEIGHT = 225.dp + + private val state by mutableStateOf(UpdateDefaultPasswordForm()) + + private class UpdateDefaultPasswordForm : Form.State() { + var oldPassword: String by mutableStateOf("") + var newPassword: String by mutableStateOf("") + var repeatPassword: String by mutableStateOf("") + + override fun cancel() = Service.driver.updateDefaultPasswordDialog.cancel() + override fun submit() = Service.driver.updateDefaultPasswordDialog.submit(oldPassword, newPassword) + override fun isValid() = oldPassword.isNotEmpty() && newPassword.isNotEmpty() + && oldPassword != newPassword && repeatPassword == newPassword + } + + @Composable + fun MayShowDialogs() { + if (Service.driver.updateDefaultPasswordDialog.isOpen) UpdateDefaultPassword() + } + + @Composable + private fun UpdateDefaultPassword() = Dialog.Layout( + state = Service.driver.connectServerDialog, + title = UPDATE_DEFAULT_PASSWORD_FOR_USERNAME.format(Service.data.connection.username).removeSuffix("."), + width = WIDTH, + height = HEIGHT + ) { + Form.Submission(state = state, modifier = Modifier.fillMaxSize(), showButtons = true) { + Form.Text(value = UPDATE_DEFAULT_PASSWORD_INSTRUCTION, softWrap = true) + OldPasswordFormField(state) + NewPasswordFormField(state) + RepeatPasswordFormField(state) + } + } + + @Composable + private fun OldPasswordFormField(state: UpdateDefaultPasswordForm) = Form.Field(label = Label.OLD_PASSWORD) { + Form.TextInput( + value = state.oldPassword, + onValueChange = { state.oldPassword = it }, + isPassword = true, + modifier = Modifier.fillMaxSize(), + ) + } + + @Composable + private fun NewPasswordFormField(state: UpdateDefaultPasswordForm) = Form.Field(label = Label.NEW_PASSWORD) { + Form.TextInput( + value = state.newPassword, + onValueChange = { state.newPassword = it }, + isPassword = true, + modifier = Modifier.fillMaxSize(), + ) + } + + @Composable + private fun RepeatPasswordFormField(state: UpdateDefaultPasswordForm) = Form.Field(label = Label.REPEAT_PASSWORD) { + Form.TextInput( + value = state.repeatPassword, + onValueChange = { state.repeatPassword = it }, + isPassword = true, + modifier = Modifier.fillMaxSize(), + ) + } +} \ No newline at end of file diff --git a/service/Service.kt b/service/Service.kt index f93056346..e46e14679 100644 --- a/service/Service.kt +++ b/service/Service.kt @@ -54,7 +54,7 @@ object Service { notification = NotificationService() confirmation = ConfirmationService() pages = PageService() - driver = DriverState(notification, preference) + driver = DriverState(notification, preference, data) project = ProjectService(preference, data, notification, confirmation, driver, pages) schema = SchemaService(driver.session, pages, notification, confirmation, status) } diff --git a/service/common/util/Label.kt b/service/common/util/Label.kt index bd10fc91f..4d43d6628 100644 --- a/service/common/util/Label.kt +++ b/service/common/util/Label.kt @@ -134,10 +134,12 @@ object Label { const val MOVE_DIRECTORY = "Move Directory" const val MS = "ms" const val NEXT_OCCURRENCE = "Next Occurrence" + const val NEW_PASSWORD = "New password" const val NO = "No" const val NONE = "None" const val NONE_IN_PARENTHESES = "($NONE)" const val NOT = "Not" + const val OLD_PASSWORD = "Old password" const val OK = "OK" const val OPEN = "Open" const val OPEN_PREFERENCES = "Open Preferences" @@ -168,6 +170,7 @@ object Label { const val PROJECT_IGNORED_PATHS = "Set Ignored Paths" const val PROJECT_MANAGER = "Project Manager" const val PROPERTY = "Property" + const val REPEAT_PASSWORD = "Repeat password" const val QUERY = "Query" const val QUERY_IS_RUNNING = "Query is Running" const val QUERY_RESPONSE_TIME = "Query Response Time" diff --git a/service/common/util/Message.kt b/service/common/util/Message.kt index 9890dea3e..5d05a34dc 100644 --- a/service/common/util/Message.kt +++ b/service/common/util/Message.kt @@ -116,6 +116,12 @@ abstract class Message(codePrefix: String, codeNumber: Int, messagePrefix: Strin Connection(17, "Failed to fetch schema for database '%s', due to:\n%s") val CREDENTIALS_EXPIRE_SOON_HOURS = Connection(18, "Your credentials are expiring within %s hour(s). Use TypeDB Console to update your password.") + val PASSWORD_UPDATED_SUCCESSFULLY = + Connection(19, "Password updated successfully.") + val RECONNECTED_WITH_NEW_PASSWORD_SUCCESSFULLY = + Connection(20, "Reconnected with new password successfully.") + val FAILED_TO_UPDATE_PASSWORD = + Connection(21, "Failed to update your password, due to: \n%s") } } diff --git a/service/common/util/Property.kt b/service/common/util/Property.kt index 5ff49d199..26706bb6c 100644 --- a/service/common/util/Property.kt +++ b/service/common/util/Property.kt @@ -44,13 +44,13 @@ object Property { } enum class Server(val displayName: String) { - TYPEDB("TypeDB"), + TYPEDB_CORE("TypeDB Core"), TYPEDB_ENTERPRISE("TypeDB Enterprise"); companion object { fun of(name: String): Server? { return when (name) { - TYPEDB.displayName -> TYPEDB + TYPEDB_CORE.displayName -> TYPEDB_CORE TYPEDB_ENTERPRISE.displayName -> TYPEDB_ENTERPRISE else -> null } @@ -86,7 +86,7 @@ object Property { fun serverOf(displayName: String): Server { return when (displayName) { - Server.TYPEDB.displayName -> Server.TYPEDB + Server.TYPEDB_CORE.displayName -> Server.TYPEDB_CORE Server.TYPEDB_ENTERPRISE.displayName -> Server.TYPEDB_ENTERPRISE else -> throw IllegalStateException("Unrecognised TypeDB server type") } diff --git a/service/common/util/Sentence.kt b/service/common/util/Sentence.kt index 8b138204e..731379590 100644 --- a/service/common/util/Sentence.kt +++ b/service/common/util/Sentence.kt @@ -194,4 +194,8 @@ object Sentence { "undefining schema, in addition to matching. " + BUTTON_ENABLED_WHEN_SESSION_OPEN const val TYPE_BROWSER_ONLY_INTERACTIVE = "The Type Browser only works in 'interactive' mode." + const val UPDATE_DEFAULT_PASSWORD_FOR_USERNAME = + "Update default password for username '%s'." + const val UPDATE_DEFAULT_PASSWORD_INSTRUCTION = + "Update your initial default password below. The new password must be different, and the repeated password must be identical." } diff --git a/service/connection/DriverState.kt b/service/connection/DriverState.kt index e4d5cf95b..a91e292a5 100644 --- a/service/connection/DriverState.kt +++ b/service/connection/DriverState.kt @@ -22,12 +22,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import com.vaticle.typedb.driver.TypeDB -import com.vaticle.typedb.driver.api.TypeDBDriver import com.vaticle.typedb.driver.api.TypeDBCredential +import com.vaticle.typedb.driver.api.TypeDBDriver import com.vaticle.typedb.driver.api.TypeDBSession import com.vaticle.typedb.driver.api.TypeDBSession.Type.DATA import com.vaticle.typedb.driver.api.TypeDBTransaction import com.vaticle.typedb.driver.common.exception.TypeDBDriverException +import com.vaticle.typedb.studio.service.common.DataService import com.vaticle.typedb.studio.service.common.NotificationService import com.vaticle.typedb.studio.service.common.NotificationService.Companion.launchAndHandle import com.vaticle.typedb.studio.service.common.PreferenceService @@ -39,6 +40,8 @@ import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companio import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.FAILED_TO_CREATE_DATABASE import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.FAILED_TO_CREATE_DATABASE_DUE_TO_DUPLICATE import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.FAILED_TO_DELETE_DATABASE +import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.FAILED_TO_UPDATE_PASSWORD +import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.PASSWORD_UPDATED_SUCCESSFULLY import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.UNABLE_TO_CONNECT import com.vaticle.typedb.studio.service.common.util.Message.Connection.Companion.UNEXPECTED_ERROR import com.vaticle.typedb.studio.service.connection.DriverState.Status.CONNECTED @@ -51,9 +54,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import mu.KotlinLogging -class DriverState constructor( +class DriverState( private val notificationSrv: NotificationService, - preferenceSrv: PreferenceService + preferenceSrv: PreferenceService, + private val dataSrv: DataService ) { enum class Status { DISCONNECTED, CONNECTED, CONNECTING } @@ -65,10 +69,37 @@ class DriverState constructor( private val LOGGER = KotlinLogging.logger {} } + class ChangeInitialPasswordDialog(val driver: DriverState) : DialogState() { + + var onCancel : (() -> Unit)? by mutableStateOf(null); private set + var onSubmit : ((old: String, new: String) -> Unit)? by mutableStateOf(null); private set + + internal fun open(onCancel: (() -> Unit)?, onSubmit: ((old: String, new: String) -> Unit)?) { + this.onCancel = onCancel + this.onSubmit = onSubmit + isOpen = true + } + + override fun close() { + isOpen = false + } + + fun cancel() { + onCancel?.invoke() + close() + } + + fun submit(old: String, new: String) { + onSubmit?.invoke(old, new) + close() + } + } + val connectServerDialog = DialogState.Base() val selectDBDialog = DialogState.Base() val manageDatabasesDialog = DialogState.Base() val manageAddressesDialog = DialogState.Base() + val updateDefaultPasswordDialog = ChangeInitialPasswordDialog(this) val status: Status get() = statusAtomic.state val isConnected: Boolean get() = status == CONNECTED val isConnecting: Boolean get() = status == CONNECTING @@ -90,28 +121,45 @@ class DriverState constructor( private val coroutines = CoroutineScope(Dispatchers.Default) - fun tryConnectToTypeDBAsync( + fun tryConnectToTypeDBCoreAsync( address: String, onSuccess: () -> Unit ) = tryConnectAsync(newConnectionName = address, onSuccess = onSuccess) { TypeDB.coreDriver(address) } fun tryConnectToTypeDBEnterpriseAsync( addresses: Set, username: String, password: String, - tlsEnabled: Boolean, onSuccess: () -> Unit - ) = tryConnectToTypeDBEnterpriseAsync(addresses, username, TypeDBCredential(username, password, tlsEnabled), onSuccess) - - fun tryConnectToTypeDBEnterpriseAsync( - addresses: Set, username: String, password: String, - caPath: String, onSuccess: () -> Unit - ) = tryConnectToTypeDBEnterpriseAsync( - addresses, username, TypeDBCredential(username, password, Path.of(caPath)), onSuccess - ) + tlsEnabled: Boolean, caPath: String, onSuccess: (() -> Unit)? = null + ) { + val credentials = if (caPath.isBlank()) TypeDBCredential(username, password, tlsEnabled) + else TypeDBCredential(username, password, Path.of(caPath)) + val postLoginFn = { + onSuccess?.invoke() + if (needsToChangeDefaultPassword()) forcePasswordUpdate() + else mayWarnPasswordExpiry() + } + tryConnectAsync(newConnectionName = "$username@${addresses.first()}", postLoginFn) { + TypeDB.enterpriseDriver(addresses, credentials) + } + } - private fun tryConnectToTypeDBEnterpriseAsync( - addresses: Set, username: String, - credentials: TypeDBCredential, onSuccess: () -> Unit - ) = tryConnectAsync(newConnectionName = "$username@${addresses.first()}", onSuccess) { - this.isEnterprise = true - TypeDB.enterpriseDriver(addresses, credentials) + private fun forcePasswordUpdate() = updateDefaultPasswordDialog.open( + onCancel = { + close() + connectServerDialog.open() + } + ) { old, new -> + tryUpdateUserPassword(old, new) { + updateDefaultPasswordDialog.close() + close() + tryConnectToTypeDBEnterpriseAsync( + addresses = dataSrv.connection.enterpriseAddresses!!.toSet(), + username = dataSrv.connection.username!!, + password = new, + tlsEnabled = dataSrv.connection.tlsEnabled!!, + caPath = dataSrv.connection.caCertificate!! + ) { + notificationSrv.info(LOGGER, Message.Connection.RECONNECTED_WITH_NEW_PASSWORD_SUCCESSFULLY) + } + } } private fun tryConnectAsync( @@ -124,7 +172,6 @@ class DriverState constructor( _driver = driverConstructor() statusAtomic.set(CONNECTED) onSuccess() - mayWarnPasswordExpiry() } catch (e: TypeDBDriverException) { statusAtomic.set(DISCONNECTED) notificationSrv.userError(LOGGER, UNABLE_TO_CONNECT, e.message ?: "") @@ -144,7 +191,8 @@ class DriverState constructor( private fun mayWarnPasswordExpiry() { if (!this.isEnterprise) return - val passwordExpiryDurationOptional: Optional? = _driver?.user()?.passwordExpirySeconds()?.map { Duration.ofSeconds(it) } + val passwordExpiryDurationOptional: Optional? = + _driver?.user()?.passwordExpirySeconds()?.map { Duration.ofSeconds(it) } if (passwordExpiryDurationOptional?.isPresent != true) return val passwordExpiryDuration = passwordExpiryDurationOptional.get() if (passwordExpiryDuration.minus(PASSWORD_EXPIRY_WARN_DURATION).isNegative) { @@ -152,6 +200,26 @@ class DriverState constructor( } } + // TODO: We need a proper way to check if default password needs to changed + fun needsToChangeDefaultPassword(): Boolean { + try { + _driver?.databases()?.all() + } catch (e: TypeDBDriverException) { + return e.toString().contains("CLS21") + } + return false + } + + fun tryUpdateUserPassword(passwordOld: String, passwordNew: String, onSuccess: (() -> Unit)?) { + try { + _driver?.user()?.passwordUpdate(passwordOld, passwordNew) + notificationSrv.info(LOGGER, PASSWORD_UPDATED_SUCCESSFULLY) + onSuccess?.invoke() + } catch (e: TypeDBDriverException) { + notificationSrv.userError(LOGGER, FAILED_TO_UPDATE_PASSWORD, e.message ?: e.toString()) + } + } + fun sendStopSignal() { session.transaction.sendStopSignal() } diff --git a/test/integration/common/StudioActions.kt b/test/integration/common/StudioActions.kt index a1199dbd9..e03722ee6 100644 --- a/test/integration/common/StudioActions.kt +++ b/test/integration/common/StudioActions.kt @@ -149,7 +149,7 @@ object StudioActions { // clicked. composeRule.onAllNodesWithText(Label.CONNECT_TO_TYPEDB).assertAll(hasClickAction()) - Service.driver.tryConnectToTypeDBAsync(address) {} + Service.driver.tryConnectToTypeDBCoreAsync(address) {} waitUntilTrue(composeRule) { Service.driver.isConnected