Use built-in oslo_db types for Columns serialized as dicts

This commit makes the following changes:
  * removes unncessary code (timeutils, oslo_utils.timeutils can
    be used instead)
  * oslo_db.types.JsonEncodedDict can be used instead of a custom
    JSONEncodedDict (forces Deckhand to save an actual dict in the
    DB as well)
  * oslo_db.types.JsonEncodedList used for new `results` Column
    in Revisions table
This commit is contained in:
Felipe Monteiro 2017-07-30 22:25:47 +01:00
parent 8e43f91751
commit 3bc589e7fc
4 changed files with 7 additions and 119 deletions

View File

@ -1,88 +0,0 @@
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Time related utilities and helper functions.
"""
import datetime
import iso8601
from monotonic import monotonic as now # noqa
from oslo_utils import encodeutils
# ISO 8601 extended time format with microseconds
_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
def isotime(at=None, subsecond=False):
"""Stringify time in ISO 8601 format."""
if not at:
at = utcnow()
st = at.strftime(_ISO8601_TIME_FORMAT
if not subsecond
else _ISO8601_TIME_FORMAT_SUBSECOND)
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
st += ('Z' if tz == 'UTC' else tz)
return st
def parse_isotime(timestr):
"""Parse time from ISO 8601 format."""
try:
return iso8601.parse_date(timestr)
except iso8601.ParseError as e:
raise ValueError(encodeutils.exception_to_unicode(e))
except TypeError as e:
raise ValueError(encodeutils.exception_to_unicode(e))
def utcnow(with_timezone=False):
"""Overridable version of utils.utcnow that can return a TZ-aware datetime.
"""
if utcnow.override_time:
try:
return utcnow.override_time.pop(0)
except AttributeError:
return utcnow.override_time
if with_timezone:
return datetime.datetime.now(tz=iso8601.iso8601.UTC)
return datetime.datetime.utcnow()
def normalize_time(timestamp):
"""Normalize time in arbitrary timezone to UTC naive object."""
offset = timestamp.utcoffset()
if offset is None:
return timestamp
return timestamp.replace(tzinfo=None) - offset
def iso8601_from_timestamp(timestamp, microsecond=False):
"""Returns an iso8601 formatted date from timestamp."""
return isotime(datetime.datetime.utcfromtimestamp(timestamp), microsecond)
utcnow.override_time = None
def delta_seconds(before, after):
"""Return the difference between two timing objects.
Compute the difference in seconds between two date, time, or
datetime objects (as a float, to microsecond resolution).
"""
delta = after - before
return datetime.timedelta.total_seconds(delta)

View File

@ -15,8 +15,7 @@
import uuid
from oslo_db.sqlalchemy import models
from oslo_log import log as logging
from oslo_serialization import jsonutils as json
from oslo_db.sqlalchemy import types as oslo_types
from oslo_utils import timeutils
from sqlalchemy import Boolean
from sqlalchemy import Column
@ -31,38 +30,12 @@ from sqlalchemy import String
from sqlalchemy import Text
from sqlalchemy.types import TypeDecorator
from deckhand.common import timeutils
LOG = logging.getLogger(__name__)
# Declarative base class which maintains a catalog of classes and tables
# relative to that base.
BASE = declarative.declarative_base()
class JSONEncodedDict(TypeDecorator):
"""Represents an immutable structure as a json-encoded string.
Usage::
JSONEncodedDict(255)
"""
impl = Text
def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
class DeckhandBase(models.ModelBase, models.TimestampMixin):
"""Base class for Deckhand Models."""
@ -133,6 +106,7 @@ class Revision(BASE, DeckhandBase):
default=lambda: str(uuid.uuid4()))
parent_id = Column(Integer, ForeignKey('revisions.id'), nullable=True)
child_id = Column(Integer, ForeignKey('revisions.id'), nullable=True)
results = Column(oslo_types.JsonEncodedList(), nullable=True)
documents = relationship("Document")
@ -154,8 +128,8 @@ class Document(BASE, DeckhandBase):
# NOTE: Do not define a maximum length for these JSON data below. However,
# this approach is not compatible with all database types.
# "metadata" is reserved, so use "doc_metadata" instead.
_metadata = Column(JSONEncodedDict(), nullable=False)
data = Column(JSONEncodedDict(), nullable=False)
_metadata = Column(oslo_types.JsonEncodedDict(), nullable=False)
data = Column(oslo_types.JsonEncodedDict(), nullable=False)
revision_id = Column(Integer, ForeignKey('revisions.id'), nullable=False)
def to_dict(self, raw_dict=False):

View File

@ -31,7 +31,9 @@ class DocumentFixture(object):
@staticmethod
def get_minimal_fixture(**kwargs):
fixture = {
'data': test_utils.rand_name('data'),
'data': {
test_utils.rand_name('key'): test_utils.rand_name('value')
},
'metadata': {
'name': test_utils.rand_name('metadata_data'),
'label': test_utils.rand_name('metadata_label'),