store what fields get persisted in trait metadata

rather than checking columns in the db

makes things more explicit
This commit is contained in:
Min RK
2023-08-08 14:52:27 +02:00
parent d9154681eb
commit 45102b248b
4 changed files with 21 additions and 168 deletions

View File

@@ -2389,13 +2389,14 @@ class JupyterHub(Application):
hub=self.hub,
)
traits = service.traits(input=True)
for key in traits:
value = orm_service.get_column(key)
if value is not None:
setattr(service, key, value)
for key, trait in traits.items():
if not trait.metadata.get("in_db", True):
continue
orm_value = getattr(orm_service, key)
if orm_value is not None:
setattr(service, key, orm_value)
if orm_service.oauth_client is not None:
service.oauth_redirect_uri = orm_service.oauth_client.redirect_uri
service.oauth_client_id = orm_service.oauth_client.identifier
service.oauth_redirect_uri = orm_service.oauth_client.redirect_uri
@@ -2476,10 +2477,15 @@ class JupyterHub(Application):
traits = service.traits(input=True)
for key, value in spec.items():
if key not in traits:
trait = traits.get(key)
if trait is None:
raise AttributeError("No such service field: %s" % key)
setattr(service, key, value)
orm_service.update_column(key, value)
# also set the value on the orm object
# unless it's marked as not in the db
# (e.g. on the oauth object)
if trait.metadata.get("in_db", True):
setattr(orm_service, key, value)
if service.api_token:
self.service_tokens[service.api_token] = service.name

View File

@@ -458,58 +458,6 @@ class Service(Base):
"""
return db.query(cls).filter(cls.name == name).first()
def _check_data_only_column(self, column_name: str) -> bool:
"""Check if a column is not a ForeignKey or in a
relationship.
Args:
column_name (str): ame of the column
Returns:
bool: returns `True` if the column is data-only,
returns `False` otherwise.
"""
if hasattr(self, column_name):
column = getattr(Service, column_name)
if hasattr(column, 'foreign_keys'):
return len(column.foreign_keys) == 0
else:
return False
else:
return False
def update_column(self, column_name: str, value: any) -> bool:
"""Update the value of a non-ForeignKey column.
Args:
column_name (str): Name of the column
value (any): New value
Returns:
bool: returns `True` if the column is updated,
returns `False` otherwise.
"""
if self._check_data_only_column(column_name):
setattr(self, column_name, value)
return True
else:
return False
def get_column(self, column_name: str):
"""Get non-ForeignKey, non-relationship column
Args:
column_name (str): Name of the column
Returns:
The value of requested column, None if it is
a foreign key or it does not exist.
"""
if self._check_data_only_column(column_name):
return getattr(self, column_name)
else:
return None
class Expiring:
"""Mixin for expiring entries

View File

@@ -180,9 +180,12 @@ class Service(LoggingConfigurable):
- user: str
The name of a system user to become.
If unspecified, run as the same user as the Hub.
"""
# inputs:
# traits tagged with `input=True` are accepted as input from configuration / API
# input traits are also persisted to the db UNLESS they are also tagged with `in_db=False`
name = Unicode(
help="""The name of the service.
@@ -205,7 +208,7 @@ class Service(LoggingConfigurable):
DEPRECATED in 3.0: use oauth_client_allowed_scopes
"""
).tag(input=True)
).tag(input=True, in_db=False)
oauth_client_allowed_scopes = List(
help="""OAuth allowed scopes.
@@ -225,7 +228,7 @@ class Service(LoggingConfigurable):
If unspecified, an API token will be generated for managed services.
"""
).tag(input=True)
).tag(input=True, in_db=False)
info = Dict(
help="""Provide a place to include miscellaneous information about the service,
@@ -310,7 +313,7 @@ class Service(LoggingConfigurable):
You shouldn't generally need to change this.
Default: `service-<name>`
"""
).tag(input=True)
).tag(input=True, in_db=False)
@default('oauth_client_id')
def _default_client_id(self):
@@ -331,7 +334,7 @@ class Service(LoggingConfigurable):
You shouldn't generally need to change this.
Default: `/services/:name/oauth_callback`
"""
).tag(input=True)
).tag(input=True, in_db=False)
@default('oauth_redirect_uri')
def _default_redirect_uri(self):

View File

@@ -145,110 +145,6 @@ def test_token_expiry(db):
assert orm_token not in user.api_tokens
@pytest.mark.parametrize(
'column, expected',
[
(
'name',
True,
),
(
'admin',
True,
),
(
'url',
True,
),
(
'oauth_client_allowed_scopes',
True,
),
(
'info',
True,
),
(
'display',
True,
),
(
'oauth_no_confirm',
True,
),
(
'command',
True,
),
(
'cwd',
True,
),
(
'environment',
True,
),
(
'user',
True,
),
(
'from_config',
True,
),
(
'api_tokens',
False,
),
(
'_server_id',
False,
),
(
'server',
False,
),
(
'pid',
True,
),
(
'oauth_client_id',
False,
),
(
'oauth_client',
False,
),
],
)
def test_service_check_data_only_column(db, column, expected):
orm_service = orm.Service(name='check_data_only_column', from_config=True)
db.add(orm_service)
db.commit()
assert orm_service._check_data_only_column(column) == expected
db.delete(orm_service)
db.commit()
def test_service_get_column(db):
orm_service = orm.Service(name='test_service_get_column', from_config=True)
db.add(orm_service)
db.commit()
orm_service.server = orm.Server()
assert orm_service.get_column('from_config')
assert orm_service.get_column('server') is None
def test_service_update_column(db):
orm_service = orm.Service(name='test_service_set_column', from_config=True)
db.add(orm_service)
db.commit()
assert orm_service.update_column('from_config', False)
assert not orm_service.update_column('server', orm.Server())
def test_service_tokens(db):
service = orm.Service(name='secret')
db.add(service)