Exemplo n.º 1
0
def document_delete(document, revision_id, bucket, session=None):
    """Delete a document

    Creates a new document with the bare minimum information about the document
    that is to be deleted, and then sets the appropriate deleted fields

    :param document: document object/dict to be deleted
    :param revision_id: id of the revision where the document is to be deleted
    :param bucket: bucket object/dict where the document will be deleted from
    :param session: Database session object.
    :return: dict representation of deleted document
    """
    session = session or get_session()

    doc = models.Document()
    # Store bare minimum information about the document.
    doc['schema'] = document['schema']
    doc['name'] = document['name']
    doc['layer'] = document['layer']
    doc['data'] = {}
    doc['meta'] = document['metadata']
    doc['data_hash'] = _make_hash({})
    doc['metadata_hash'] = _make_hash({})
    doc['bucket_id'] = bucket['id']
    doc['revision_id'] = revision_id

    # Save and mark the document as `deleted` in the database.
    try:
        doc.save(session=session)
    except db_exception.DBDuplicateEntry:
        raise errors.DuplicateDocumentExists(schema=doc['schema'],
                                             layer=doc['layer'],
                                             name=doc['name'],
                                             bucket=bucket['name'])
    doc.safe_delete(session=session)

    return doc.to_dict()
Exemplo n.º 2
0
def revision_rollback(revision_id, latest_revision, session=None):
    """Rollback the latest revision to revision specified by ``revision_id``.

    Rolls back the latest revision to the revision specified by ``revision_id``
    thereby creating a new, carbon-copy revision.

    :param revision_id: Revision ID to which to rollback.
    :param latest_revision: Dictionary representation of the latest revision
        in the system.
    :returns: The newly created revision.
    """
    session = session or get_session()
    latest_revision_hashes = [(d['data_hash'], d['metadata_hash'])
                              for d in latest_revision['documents']]

    if latest_revision['id'] == revision_id:
        LOG.debug('The revision being rolled back to is the current revision.'
                  'Expect no meaningful changes.')

    if revision_id == 0:
        # Placeholder revision as revision_id=0 doesn't exist.
        orig_revision = {'documents': []}
    else:
        orig_revision = revision_get(revision_id, session=session)

    # A mechanism for determining whether a particular document has changed
    # between revisions. Keyed with the document_id, the value is True if
    # it has changed, else False.
    doc_diff = {}
    for orig_doc in orig_revision['documents']:
        if ((orig_doc['data_hash'], orig_doc['metadata_hash'])
                not in latest_revision_hashes):
            doc_diff[orig_doc['id']] = True
        else:
            doc_diff[orig_doc['id']] = False

    # No changes have been made between the target revision to rollback to
    # and the latest revision.
    if set(doc_diff.values()) == set([False]):
        LOG.debug('The revision being rolled back to has the same documents '
                  'as that of the current revision. Expect no meaningful '
                  'changes.')

    # Create the new revision,
    new_revision = models.Revision()
    with session.begin():
        new_revision.save(session=session)

    # Create the documents for the revision.
    for orig_document in orig_revision['documents']:
        orig_document['revision_id'] = new_revision['id']
        orig_document['meta'] = orig_document.pop('metadata')

        new_document = models.Document()
        new_document.update({
            x: orig_document[x]
            for x in ('name', 'meta', 'layer', 'data', 'data_hash',
                      'metadata_hash', 'schema', 'bucket_id')
        })
        new_document['revision_id'] = new_revision['id']

        # If the document has changed, then use the revision_id of the new
        # revision, otherwise use the original revision_id to preserve the
        # revision history.
        if doc_diff[orig_document['id']]:
            new_document['orig_revision_id'] = new_revision['id']
        else:
            new_document['orig_revision_id'] = orig_revision['id']

        with session.begin():
            new_document.save(session=session)

    new_revision = new_revision.to_dict()
    new_revision['documents'] = _update_revision_history(
        new_revision['documents'])

    return new_revision
Exemplo n.º 3
0
 def _document_create(document):
     model = models.Document()
     model.update(document)
     return model
Exemplo n.º 4
0
def documents_create(bucket_name, documents, session=None):
    """Create a set of documents and associated bucket.

    If no changes are detected, a new revision will not be created. This
    allows services to periodically re-register their schemas without
    creating unnecessary revisions.

    :param bucket_name: The name of the bucket with which to associate created
        documents.
    :param documents: List of documents to be created.
    :param session: Database session object.
    :returns: List of created documents in dictionary format.
    :raises DocumentExists: If the document already exists in the DB for any
        bucket.
    """
    session = session or get_session()
    resp = []

    with session.begin():
        documents_to_create = _documents_create(bucket_name,
                                                documents,
                                                session=session)

        # The documents to be deleted are computed by comparing the documents
        # for the previous revision (if it exists) that belong to `bucket_name`
        # with `documents`: the difference between the former and the latter.
        document_history = [
            d for d in revision_documents_get(bucket_name=bucket_name,
                                              session=session)
        ]
        documents_to_delete = [
            h for h in document_history
            if eng_utils.meta(h) not in [eng_utils.meta(d) for d in documents]
        ]

        # Only create a revision if any docs have been created, changed or
        # deleted.
        if any([documents_to_create, documents_to_delete]):
            revision = revision_create(session=session)
            bucket = bucket_get_or_create(bucket_name, session=session)

        if documents_to_delete:
            LOG.debug('Deleting documents: %s.',
                      [eng_utils.meta(d) for d in documents_to_delete])
            deleted_documents = []

            for d in documents_to_delete:
                doc = models.Document()
                # Store bare minimum information about the document.
                doc['schema'] = d['schema']
                doc['name'] = d['name']
                doc['layer'] = d['layer']
                doc['data'] = {}
                doc['meta'] = d['metadata']
                doc['data_hash'] = _make_hash({})
                doc['metadata_hash'] = _make_hash({})
                doc['bucket_id'] = bucket['id']
                doc['revision_id'] = revision['id']

                # Save and mark the document as `deleted` in the database.
                try:
                    doc.save(session=session)
                except db_exception.DBDuplicateEntry:
                    raise errors.DuplicateDocumentExists(schema=doc['schema'],
                                                         layer=doc['layer'],
                                                         name=doc['name'],
                                                         bucket=bucket['name'])
                doc.safe_delete(session=session)
                deleted_documents.append(doc)
                resp.append(doc.to_dict())

        if documents_to_create:
            LOG.debug('Creating documents: %s.',
                      [(d['schema'], d['layer'], d['name'])
                       for d in documents_to_create])
            for doc in documents_to_create:
                doc['bucket_id'] = bucket['id']
                doc['revision_id'] = revision['id']
                if not doc.get('orig_revision_id'):
                    doc['orig_revision_id'] = doc['revision_id']

                try:
                    doc.save(session=session)
                except db_exception.DBDuplicateEntry:
                    raise errors.DuplicateDocumentExists(schema=doc['schema'],
                                                         layer=doc['layer'],
                                                         name=doc['name'],
                                                         bucket=bucket['name'])

                resp.append(doc.to_dict())
    # NOTE(fmontei): The orig_revision_id is not copied into the
    # revision_id for each created document, because the revision_id here
    # should reference the just-created revision. In case the user needs
    # the original revision_id, that is returned as well.

    return resp
Exemplo n.º 5
0
 def _document_create(values):
     document = models.Document()
     with session.begin():
         document.update(values)
     return document
Exemplo n.º 6
0
def documents_create(bucket_name, documents, validations=None,
                     session=None):
    """Create a set of documents and associated bucket.

    If no changes are detected, a new revision will not be created. This
    allows services to periodically re-register their schemas without
    creating unnecessary revisions.

    :param bucket_name: The name of the bucket with which to associate created
        documents.
    :param documents: List of documents to be created.
    :param validation_policies: List of validation policies to be created.
    :param session: Database session object.
    :returns: List of created documents in dictionary format.
    :raises DocumentExists: If the (document.schema, document.metadata.name)
        already exists in another bucket.
    """
    session = session or get_session()
    documents_to_create = _documents_create(bucket_name, documents, session)

    resp = []

    # The documents to be deleted are computed by comparing the documents for
    # the previous revision (if it exists) that belong to `bucket_name` with
    # `documents`: the difference between the former and the latter.
    document_history = [(d['schema'], d['name'])
                        for d in revision_documents_get(
                            bucket_name=bucket_name)]
    documents_to_delete = [
        h for h in document_history if h not in
        [(d['schema'], d['metadata']['name']) for d in documents]]

    # Only create a revision if any docs have been created, changed or deleted.
    if any([documents_to_create, documents_to_delete]):
        bucket = bucket_get_or_create(bucket_name)
        revision = revision_create()
        if validations:
            for validation in validations:
                validation_create(revision['id'], validation['name'],
                                  validation)

    if documents_to_delete:
        LOG.debug('Deleting documents: %s.', documents_to_delete)
        deleted_documents = []

        for d in documents_to_delete:
            doc = models.Document()
            with session.begin():
                # Store bare minimum information about the document.
                doc['schema'] = d[0]
                doc['name'] = d[1]
                doc['data'] = {}
                doc['_metadata'] = {}
                doc['data_hash'] = _make_hash({})
                doc['metadata_hash'] = _make_hash({})
                doc['bucket_id'] = bucket['id']
                doc['revision_id'] = revision['id']

                # Save and mark the document as `deleted` in the database.
                doc.save(session=session)
                doc.safe_delete(session=session)
                deleted_documents.append(doc)
            resp.append(doc.to_dict())

    if documents_to_create:
        LOG.debug('Creating documents: %s.',
                  [(d['schema'], d['name']) for d in documents_to_create])
        for doc in documents_to_create:
            with session.begin():
                doc['bucket_id'] = bucket['id']
                doc['revision_id'] = revision['id']
                doc.save(session=session)
            resp.append(doc.to_dict())
    # NOTE(fmontei): The orig_revision_id is not copied into the
    # revision_id for each created document, because the revision_id here
    # should reference the just-created revision. In case the user needs
    # the original revision_id, that is returned as well.

    return resp
Exemplo n.º 7
0
 def _document_create(document):
     model = models.Document()
     with session.begin():
         model.update(document)
     return model
Exemplo n.º 8
0
def revision_rollback(revision_id, latest_revision, session=None):
    """Rollback the latest revision to revision specified by ``revision_id``.

    Rolls back the latest revision to the revision specified by ``revision_id``
    thereby creating a new, carbon-copy revision.

    :param revision_id: Revision ID to which to rollback.
    :param latest_revision: Dictionary representation of the latest revision
        in the system.
    :returns: The newly created revision.
    """
    session = session or get_session()
    latest_revision_hashes = [(d['data_hash'], d['metadata_hash'])
                              for d in latest_revision['documents']]

    # If the rollback revision is the same as the latest revision, then there's
    # no point in rolling back.
    if latest_revision['id'] == revision_id:
        raise errors.InvalidRollback(revision_id=revision_id)

    orig_revision = revision_get(revision_id, session=session)

    # A mechanism for determining whether a particular document has changed
    # between revisions. Keyed with the document_id, the value is True if
    # it has changed, else False.
    doc_diff = {}
    for orig_doc in orig_revision['documents']:
        if ((orig_doc['data_hash'], orig_doc['metadata_hash'])
                not in latest_revision_hashes):
            doc_diff[orig_doc['id']] = True
        else:
            doc_diff[orig_doc['id']] = False

    # If no changes have been made between the target revision to rollback to
    # and the latest revision, raise an exception.
    if set(doc_diff.values()) == set([False]):
        raise errors.InvalidRollback(revision_id=revision_id)

    # Create the new revision,
    new_revision = models.Revision()
    with session.begin():
        new_revision.save(session=session)

    # Create the documents for the revision.
    for orig_document in orig_revision['documents']:
        orig_document['revision_id'] = new_revision['id']
        orig_document['_metadata'] = orig_document.pop('metadata')

        new_document = models.Document()
        new_document.update({
            x: orig_document[x]
            for x in ('name', '_metadata', 'data', 'data_hash',
                      'metadata_hash', 'schema', 'bucket_id')
        })
        new_document['revision_id'] = new_revision['id']

        # If the document has changed, then use the revision_id of the new
        # revision, otherwise use the original revision_id to preserve the
        # revision history.
        if doc_diff[orig_document['id']]:
            new_document['orig_revision_id'] = new_revision['id']
        else:
            new_document['orig_revision_id'] = orig_revision['id']

        with session.begin():
            new_document.save(session=session)

    new_revision = new_revision.to_dict()
    new_revision['documents'] = _update_revision_history(
        new_revision['documents'])

    return new_revision
Exemplo n.º 9
0
def revision_rollback(revision_id, latest_revision, session=None):
    """Rollback the latest revision to revision specified by ``revision_id``.

    Rolls back the latest revision to the revision specified by ``revision_id``
    thereby creating a new, carbon-copy revision.

    :param revision_id: Revision ID to which to rollback.
    :param latest_revision: Dictionary representation of the latest revision
        in the system.
    :returns: The newly created revision.
    """
    session = session or get_session()
    latest_revision_docs = revision_documents_get(latest_revision['id'],
                                                  session=session)
    latest_revision_hashes = [(d['data_hash'], d['metadata_hash'])
                              for d in latest_revision_docs]

    if latest_revision['id'] == revision_id:
        LOG.debug('The revision being rolled back to is the current revision.'
                  'Expect no meaningful changes.')

    if revision_id == 0:
        # Delete all existing documents in all buckets
        all_buckets = bucket_get_all(deleted=False)
        bucket_names = [str(b['name']) for b in all_buckets]
        revision = documents_delete_from_buckets_list(bucket_names,
                                                      session=session)

        return revision.to_dict()
    else:
        # Sorting the documents so the documents in the new revision are in
        # the same order as the previous revision to support stable testing
        orig_revision_docs = sorted(revision_documents_get(revision_id,
                                                           session=session),
                                    key=lambda d: d['id'])

    # A mechanism for determining whether a particular document has changed
    # between revisions. Keyed with the document_id, the value is True if
    # it has changed, else False.
    doc_diff = {}
    # List of unique buckets that exist in this revision
    unique_buckets = []
    for orig_doc in orig_revision_docs:
        if ((orig_doc['data_hash'], orig_doc['metadata_hash'])
                not in latest_revision_hashes):
            doc_diff[orig_doc['id']] = True
        else:
            doc_diff[orig_doc['id']] = False
        if orig_doc['bucket_id'] not in unique_buckets:
            unique_buckets.append(orig_doc['bucket_id'])

    # We need to find which buckets did not exist at this revision
    buckets_to_delete = []
    all_buckets = bucket_get_all(deleted=False)
    for bucket in all_buckets:
        if bucket['id'] not in unique_buckets:
            buckets_to_delete.append(str(bucket['name']))

    # Create the new revision,
    if len(buckets_to_delete) > 0:
        new_revision = documents_delete_from_buckets_list(buckets_to_delete,
                                                          session=session)
    else:
        new_revision = models.Revision()
        with session.begin():
            new_revision.save(session=session)

    # No changes have been made between the target revision to rollback to
    # and the latest revision.
    if set(doc_diff.values()) == set([False]):
        LOG.debug('The revision being rolled back to has the same documents '
                  'as that of the current revision. Expect no meaningful '
                  'changes.')

    # Create the documents for the revision.
    for orig_document in orig_revision_docs:
        orig_document['revision_id'] = new_revision['id']
        orig_document['meta'] = orig_document.pop('metadata')

        new_document = models.Document()
        new_document.update({
            x: orig_document[x]
            for x in ('name', 'meta', 'layer', 'data', 'data_hash',
                      'metadata_hash', 'schema', 'bucket_id')
        })
        new_document['revision_id'] = new_revision['id']

        # If the document has changed, then use the revision_id of the new
        # revision, otherwise use the original revision_id to preserve the
        # revision history.
        if doc_diff[orig_document['id']]:
            new_document['orig_revision_id'] = new_revision['id']
        else:
            new_document['orig_revision_id'] = revision_id

        with session.begin():
            new_document.save(session=session)

    new_revision = new_revision.to_dict()
    new_revision['documents'] = _update_revision_history(
        new_revision['documents'])

    return new_revision