From 3bc589e7fc7a8bd78d90246732ef50db8b16a45c Mon Sep 17 00:00:00 2001 From: Felipe Monteiro Date: Sun, 30 Jul 2017 22:25:47 +0100 Subject: [PATCH] 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 --- deckhand/common/__init__.py | 0 deckhand/common/timeutils.py | 88 ------------------------ deckhand/db/sqlalchemy/models.py | 34 ++------- deckhand/tests/unit/db/test_documents.py | 4 +- 4 files changed, 7 insertions(+), 119 deletions(-) delete mode 100644 deckhand/common/__init__.py delete mode 100644 deckhand/common/timeutils.py diff --git a/deckhand/common/__init__.py b/deckhand/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/deckhand/common/timeutils.py b/deckhand/common/timeutils.py deleted file mode 100644 index 37a2eace..00000000 --- a/deckhand/common/timeutils.py +++ /dev/null @@ -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) diff --git a/deckhand/db/sqlalchemy/models.py b/deckhand/db/sqlalchemy/models.py index 7c938911..ccd1a2dc 100644 --- a/deckhand/db/sqlalchemy/models.py +++ b/deckhand/db/sqlalchemy/models.py @@ -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): diff --git a/deckhand/tests/unit/db/test_documents.py b/deckhand/tests/unit/db/test_documents.py index 2ae5053d..4fa1dd4f 100644 --- a/deckhand/tests/unit/db/test_documents.py +++ b/deckhand/tests/unit/db/test_documents.py @@ -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'),