mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-16 14:33:00 +00:00
store what fields get persisted in trait metadata
rather than checking columns in the db makes things more explicit
This commit is contained in:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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):
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user