Closes #247: Adapter config changes are now only persisted after clicking Edit

This commit is contained in:
Sumner Evans
2020-07-17 13:01:57 -06:00
parent 0f8239cf08
commit e86ba55a7a
3 changed files with 60 additions and 24 deletions

View File

@@ -1,4 +1,5 @@
import abc
import copy
import hashlib
import uuid
from dataclasses import dataclass
@@ -174,18 +175,47 @@ class ConfigurationStore(dict):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._changed_secrets_store = {}
def __repr__(self) -> str:
values = ", ".join(f"{k}={v!r}" for k, v in sorted(self.items()))
return f"ConfigurationStore({values})"
def clone(self) -> "ConfigurationStore":
configuration_store = ConfigurationStore(**copy.deepcopy(self))
configuration_store._changed_secrets_store = copy.deepcopy(
self._changed_secrets_store
)
return configuration_store
def persist_secrets(self):
if not keyring_imported or ConfigurationStore.MOCK:
return
for key, secret in self._changed_secrets_store.items():
try:
password_id = None
if password_type_and_id := self.get(key):
if cast(List[str], password_type_and_id)[0] == "keyring":
password_id = password_type_and_id[1]
if password_id is None:
password_id = str(uuid.uuid4())
keyring.set_password(KEYRING_APP_NAME, password_id, secret)
self[key] = ["keyring", password_id]
except Exception:
return
def get_secret(self, key: str) -> Optional[str]:
"""
Get the secret value in the store with the given key. If the key doesn't exist
in the store, return the default. This will retrieve the secret from whatever is
configured as the underlying secret storage mechanism so you don't have to deal
with secret storage yourself.
Get the secret value in the store with the given key. This will retrieve the
secret from whatever is configured as the underlying secret storage mechanism so
you don't have to deal with secret storage yourself.
"""
if secret := self._changed_secrets_store.get(key):
return secret
value = self.get(key)
if not isinstance(value, list) or len(value) != 2:
return None
@@ -199,26 +229,11 @@ class ConfigurationStore(dict):
def set_secret(self, key: str, value: str = None):
"""
Set the secret value of the given key in the store. This should be used for
things such as passwords or API tokens. This will store the secret in whatever
is configured as the underlying secret storage mechanism so you don't have to
deal with secret storage yourself.
things such as passwords or API tokens. When :class:`persist_secrets` is called,
the secrets will be stored in whatever is configured as the underlying secret
storage mechanism so you don't have to deal with secret storage yourself.
"""
if keyring_imported and not ConfigurationStore.MOCK:
try:
password_id = None
if password_type_and_id := self.get(key):
if cast(List[str], password_type_and_id)[0] == "keyring":
password_id = password_type_and_id[1]
if password_id is None:
password_id = str(uuid.uuid4())
keyring.set_password(KEYRING_APP_NAME, password_id, value)
self[key] = ["keyring", password_id]
return
except Exception:
pass
self._changed_secrets_store[key] = value
self[key] = ["plaintext", value]

View File

@@ -594,7 +594,7 @@ class SublimeMusicApp(Gtk.Application):
self.show_configure_servers_dialog()
def on_edit_current_music_provider(self, *args):
self.show_configure_servers_dialog(self.app_config.provider)
self.show_configure_servers_dialog(self.app_config.provider.clone())
def on_switch_music_provider(self, _, provider_id: GLib.Variant):
if self.app_config.state.playing:
@@ -983,7 +983,9 @@ class SublimeMusicApp(Gtk.Application):
if result == Gtk.ResponseType.APPLY:
assert dialog.provider_config is not None
provider_id = dialog.provider_config.id
dialog.provider_config.persist_secrets()
self.app_config.providers[provider_id] = dialog.provider_config
self.app_config.save()
if provider_id == self.app_config.current_provider_id:
# Just update the window.

View File

@@ -47,6 +47,25 @@ class ProviderConfiguration:
if self.caching_adapter_type:
self.caching_adapter_type.migrate_configuration(self.caching_adapter_config)
def clone(self) -> "ProviderConfiguration":
return ProviderConfiguration(
self.id,
self.name,
self.ground_truth_adapter_type,
self.ground_truth_adapter_config.clone(),
self.caching_adapter_type,
(
self.caching_adapter_config.clone()
if self.caching_adapter_config
else None
),
)
def persist_secrets(self):
self.ground_truth_adapter_config.persist_secrets()
if self.caching_adapter_config:
self.caching_adapter_config.persist_secrets()
def encode_providers(
providers_dict: Dict[str, Dict[str, Any]]