artifacts: refactor metadata code

- fix type conversions
- split some code so we can re-use it
- add specific validations of duplicates and wrong types
This commit is contained in:
Marcin Kuzminski 2019-10-17 13:26:31 +02:00
parent 1d91e6557d
commit fcdef0dd05
2 changed files with 64 additions and 13 deletions

View file

@ -157,3 +157,15 @@ class VCSServerUnavailable(HTTPBadGateway):
if message:
self.explanation += ': ' + message
super(VCSServerUnavailable, self).__init__()
class ArtifactMetadataDuplicate(ValueError):
def __init__(self, *args, **kwargs):
self.err_section = kwargs.pop('err_section', None)
self.err_key = kwargs.pop('err_key', None)
super(ArtifactMetadataDuplicate, self).__init__(*args, **kwargs)
class ArtifactMetadataBadValueType(ValueError):
pass

View file

@ -67,6 +67,8 @@ from rhodecode.lib.ext_json import json
from rhodecode.lib.caching_query import FromCache
from rhodecode.lib.encrypt import AESCipher, validate_and_get_enc_data
from rhodecode.lib.encrypt2 import Encryptor
from rhodecode.lib.exceptions import (
ArtifactMetadataDuplicate, ArtifactMetadataBadValueType)
from rhodecode.model.meta import Base, Session
URL_SEP = '/'
@ -5145,6 +5147,10 @@ class FileStore(Base, BaseModel):
nullable=True, unique=None, default=None)
repo_group = relationship('RepoGroup', lazy='joined')
@classmethod
def get_by_store_uid(cls, file_store_uid):
return FileStore.query().filter(FileStore.file_uid == file_store_uid).scalar()
@classmethod
def create(cls, file_uid, filename, file_hash, file_size, file_display_name='',
file_description='', enabled=True, hidden=False, check_acl=True,
@ -5176,6 +5182,19 @@ class FileStore(Base, BaseModel):
return
for section, key, value, value_type in args:
has_key = FileStoreMetadata().query() \
.filter(FileStoreMetadata.file_store_id == file_store.file_store_id) \
.filter(FileStoreMetadata.file_store_meta_section == section) \
.filter(FileStoreMetadata.file_store_meta_key == key) \
.scalar()
if has_key:
msg = 'key `{}` already defined under section `{}` for this file.'\
.format(key, section)
raise ArtifactMetadataDuplicate(msg, err_section=section, err_key=key)
# NOTE(marcink): raises ArtifactMetadataBadValueType
FileStoreMetadata.valid_value_type(value_type)
meta_entry = FileStoreMetadata()
meta_entry.file_store = file_store
meta_entry.file_store_meta_section = section
@ -5185,8 +5204,12 @@ class FileStore(Base, BaseModel):
Session().add(meta_entry)
if commit:
Session().commit()
try:
if commit:
Session().commit()
except IntegrityError:
Session().rollback()
raise ArtifactMetadataDuplicate('Duplicate section/key found for this file.')
@classmethod
def bump_access_counter(cls, file_uid, commit=True):
@ -5239,20 +5262,28 @@ class FileStoreMetadata(Base, BaseModel):
file_store = relationship('FileStore', lazy='joined')
@classmethod
def valid_value_type(cls, value):
if value.split('.')[0] not in cls.SETTINGS_TYPES:
raise ArtifactMetadataBadValueType(
'value_type must be one of %s got %s' % (cls.SETTINGS_TYPES.keys(), value))
@hybrid_property
def file_store_meta_value(self):
v = self._file_store_meta_value
_type = self._file_store_meta_value
if _type:
val = self._file_store_meta_value
if self._file_store_meta_value_type:
# e.g unicode.encrypted == unicode
_type = self._file_store_meta_value.split('.')[0]
# decode the encrypted value
_type = self._file_store_meta_value_type.split('.')[0]
# decode the encrypted value if it's encrypted field type
if '.encrypted' in self._file_store_meta_value_type:
cipher = EncryptedTextValue()
v = safe_unicode(cipher.process_result_value(v, None))
val = safe_unicode(cipher.process_result_value(val, None))
# do final type conversion
converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
val = converter(val)
converter = self.SETTINGS_TYPES.get(_type) or self.SETTINGS_TYPES['unicode']
return converter(v)
return val
@file_store_meta_value.setter
def file_store_meta_value(self, val):
@ -5270,11 +5301,19 @@ class FileStoreMetadata(Base, BaseModel):
@file_store_meta_value_type.setter
def file_store_meta_value_type(self, val):
# e.g unicode.encrypted
if val.split('.')[0] not in self.SETTINGS_TYPES:
raise Exception('type must be one of %s got %s'
% (self.SETTINGS_TYPES.keys(), val))
self.valid_value_type(val)
self._file_store_meta_value_type = val
def __json__(self):
data = {
'artifact': self.file_store.file_uid,
'section': self.file_store_meta_section,
'key': self.file_store_meta_key,
'value': self.file_store_meta_value,
}
return data
def __repr__(self):
return '<%s[%s]%s=>%s]>' % (self.__class__.__name__, self.file_store_meta_section,
self.file_store_meta_key, self.file_store_meta_value)