diff --git a/deckhand/db/sqlalchemy/__init__.py b/deckhand/db/sqlalchemy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/deckhand/db/api_models.py b/deckhand/db/sqlalchemy/api_models.py similarity index 59% rename from deckhand/db/api_models.py rename to deckhand/db/sqlalchemy/api_models.py index f99e3b90..9183a8a5 100644 --- a/deckhand/db/api_models.py +++ b/deckhand/db/sqlalchemy/api_models.py @@ -12,14 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json - from oslo_db.sqlalchemy import models +from oslo_serialization import jsonutils as json from oslo_utils import timeutils -import sqlalchemy as sa +from sqlalchemy import Column from sqlalchemy.ext import declarative -from sqlalchemy import orm as sa_orm -from sqlalchemy import types as sa_types +from sqlalchemy import Integer +from sqlalchemy import orm +from sqlalchemy import schema +from sqlalchemy import String +from sqlalchemy import types class _DeckhandBase(models.ModelBase, models.TimestampMixin): @@ -31,7 +33,7 @@ class _DeckhandBase(models.ModelBase, models.TimestampMixin): API_BASE = declarative.declarative_base(cls=_DeckhandBase) -class JSONEncodedDict(sa_types.TypeDecorator): +class JSONEncodedDict(types.TypeDecorator): """Represents an immutable structure as a json-encoded string. Usage:: @@ -40,7 +42,7 @@ class JSONEncodedDict(sa_types.TypeDecorator): """ - impl = sa_types.VARCHAR + impl = types.VARCHAR def process_bind_param(self, value, dialect): if value is not None: @@ -56,10 +58,15 @@ class JSONEncodedDict(sa_types.TypeDecorator): class Document(API_BASE): __tablename__ = 'document' + __table_args__ = (schema.UniqueConstraint('schema_version', 'kind', + name='uniq_schema_version_kinds0schema_version0kind'),) - id = sa.Column(sa.String(255), primary_key=True, autoincrement=True) - revision_index = sa.Column(sa.Integer, nullable=False) - document_schema = sa.Column(sa.String(64), nullable=False) - instance_key = sa.Column(sa.String(64), nullable=False, unique=True) - document_metadata = sa.Column(JSONEncodedDict(), nullable=False) - document_data = sa.Column(JSONEncodedDict(), nullable=False) + id = Column(String(255), primary_key=True, autoincrement=True) + revision_index = Column(Integer, nullable=False) + schema_version = Column(String(64), nullable=False) + kind = Column(String(64), nullable=False) + # 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. + doc_metadata = Column(JSONEncodedDict(), nullable=False) + data = Column(JSONEncodedDict(), nullable=False) diff --git a/deckhand/objects/base.py b/deckhand/objects/base.py index 8690ecfa..4977664f 100644 --- a/deckhand/objects/base.py +++ b/deckhand/objects/base.py @@ -128,9 +128,6 @@ class DeckhandPayloadBase(DeckhandPersistentObject): if isinstance(source, dict): source = self._dict_to_obj(source) try: - LOG.debug(source) - LOG.debug(field) - setattr(self, key, getattr(source, field)) # ObjectActionError - not lazy loadable field # NotImplementedError - obj_load_attr() is not even defined @@ -144,7 +141,7 @@ class DeckhandPayloadBase(DeckhandPersistentObject): {'field': key, 'payload': self.__class__.__name__, 'exception': e}) - # NOTE(gibi): This will fail if the payload field is not + # NOTE: This will fail if the payload field is not # nullable, but that means that either the source object is not # properly initialized or the payload field needs to be defined # as nullable diff --git a/deckhand/objects/documents.py b/deckhand/objects/documents.py index 7f96d559..3cd8d3f2 100644 --- a/deckhand/objects/documents.py +++ b/deckhand/objects/documents.py @@ -19,7 +19,7 @@ from oslo_log import log as logging import oslo_versionedobjects.fields as ovo_fields -from deckhand.db import api_models +from deckhand.db.sqlalchemy import api_models from deckhand import objects from deckhand.objects import base from deckhand.objects import fields as deckhand_fields @@ -30,7 +30,8 @@ LOG = logging.getLogger(__name__) class DocumentPayload(base.DeckhandPayloadBase): SCHEMA = { - #'instance_key': ('document', 'instance_key'), + 'schema_version': ('document', 'schemaVersion'), + 'kind': ('document', 'kind'), 'metadata': ('document', 'metadata'), 'data': ('document', 'data') } @@ -39,7 +40,8 @@ class DocumentPayload(base.DeckhandPayloadBase): VERSION = '1.0' fields = { - #'instance_key': ovo_fields.StringField(nullable=False), + 'schema_version': ovo_fields.StringField(nullable=False), + 'kind': ovo_fields.StringField(nullable=False), 'metadata': ovo_fields.DictOfStringsField(nullable=False), 'data': ovo_fields.DictOfStringsField(nullable=False) } @@ -58,7 +60,7 @@ class Document(base.DeckhandPersistentObject, base.DeckhandObject): fields = { 'id': ovo_fields.IntegerField(nullable=False, read_only=True), - 'blob': ovo_fields.ObjectField('DocumentPayload', nullable=False), + 'document': ovo_fields.ObjectField('DocumentPayload', nullable=False), 'revision_index': ovo_fields.NonNegativeIntegerField(nullable=False), 'status': ovo_fields.StringField(nullable=False) } @@ -69,7 +71,24 @@ class Document(base.DeckhandPersistentObject, base.DeckhandObject): self.obj_reset_changes() def create(self, document): - #updates = self.obj_get_changes() LOG.debug(document) - self.blob = DocumentPayload(document) - #api_models.Document() + self.document = DocumentPayload(document) + + if not self.document.populated: + return + + payload = { + 'revision_index': 0, + 'schema_version': self.document.schema_version, + 'kind': self.document.kind, + 'doc_metadata': self.document.metadata, + 'data': self.document.data + } + db_document = api_models.Document() + db_document.update(payload) + + try: + # Need to pass session context + db_document.save() + except Exception as e: + LOG.exception(e)