예제 #1
0
    def _show_revision(self, req, resp, revision_id):
        """Returns detailed description of a particular revision.

        The status of each ValidationPolicy belonging to the revision is also
        included.
        """
        try:
            revision = db_api.revision_get(revision_id)
        except errors.RevisionNotFound as e:
            raise falcon.HTTPNotFound(description=e.format_message())

        revision_resp = self.view_builder.show(revision)
        resp.status = falcon.HTTP_200
        resp.body = revision_resp
예제 #2
0
 def show_revision(self, revision_id):
     revision = db_api.revision_get(revision_id)
     self.validate_revision(revision)
     return revision
예제 #3
0
def revision_diff(revision_id, comparison_revision_id, deepdiff=False):
    """Generate the diff between two revisions.

    Generate the diff between the two revisions: `revision_id` and
    `comparison_revision_id`.
    a. When deepdiff=False: A basic comparison of the revisions in terms of
    how the buckets involved have changed is generated. Only buckets with
    existing documents in either of the two revisions in question will be
    reported.
    b. When deepdiff=True: Along with basic comparision, It will generate deep
    diff between revisions' modified buckets.

    Only in case of diff, The ordering of the two revision IDs is
    interchangeable, i.e. no matter the order, the same result is generated.

    The differences include:

        - "created": A bucket has been created between the revisions.
        - "deleted": A bucket has been deleted between the revisions.
        - "modified": A bucket has been modified between the revisions.
                      When deepdiff is enabled, It also includes deep
                      difference between the revisions.
        - "unmodified": A bucket remains unmodified between the revisions.

    :param revision_id: ID of the first revision.
    :param comparison_revision_id: ID of the second revision.
    :param deepdiff: Whether deepdiff needed or not.
    :returns: A dictionary, keyed with the bucket IDs, containing any of the
        differences enumerated above.

    Examples Diff::

        # GET /api/v1.0/revisions/6/diff/3
        bucket_a: created
        bucket_b: deleted
        bucket_c: modified
        bucket_d: unmodified

        # GET /api/v1.0/revisions/0/diff/6
        bucket_a: created
        bucket_c: created
        bucket_d: created

        # GET /api/v1.0/revisions/6/diff/6
        bucket_a: unmodified
        bucket_c: unmodified
        bucket_d: unmodified

        # GET /api/v1.0/revisions/0/diff/0
        {}

    Examples DeepDiff::

        # GET /api/v1.0/revisions/3/deepdiff/4
        bucket_a: modified
        bucket_a diff:
          document_changed:
            count: 1
            details:
              ('example/Kind/v1', 'doc-b'):
                data_changed:
                  values_changed:
                    root['foo']: {new_value: 3, old_value: 2}
                metadata_changed: {}

        # GET /api/v1.0/revisions/2/deepdiff/3
        bucket_a: modified
        bucket_a diff:
          document_added:
            count: 1
            details:
            - [example/Kind/v1, doc-c]

        # GET /api/v1.0/revisions/0/deepdiff/0
        {}

        # GET /api/v1.0/revisions/0/deepdiff/3
        bucket_a: created
    """
    if deepdiff:
        docs = (_rendered_doc(revision_id) if revision_id != 0 else [])
        comparison_docs = (_rendered_doc(comparison_revision_id)
                           if comparison_revision_id != 0 else [])
    else:
        # Retrieve document history for each revision. Since `revision_id` of 0
        # doesn't exist, treat it as a special case: empty list.
        docs = (db_api.revision_documents_get(
            revision_id, include_history=True, unique_only=False)
                if revision_id != 0 else [])
        comparison_docs = (db_api.revision_documents_get(
            comparison_revision_id, include_history=True, unique_only=False)
                           if comparison_revision_id != 0 else [])

    # Remove each deleted document and its older counterparts because those
    # documents technically don't exist.
    docs = utils.exclude_deleted_documents(docs)
    comparison_docs = utils.exclude_deleted_documents(comparison_docs)

    revision = db_api.revision_get(revision_id) if revision_id != 0 else None
    comparison_revision = (db_api.revision_get(comparison_revision_id)
                           if comparison_revision_id != 0 else None)

    # Each dictionary below, keyed with the bucket's name, references the list
    # of documents related to each bucket.
    buckets = {}
    comparison_buckets = {}
    for doc in docs:
        buckets.setdefault(doc['bucket_name'], [])
        buckets[doc['bucket_name']].append(doc)
    for doc in comparison_docs:
        comparison_buckets.setdefault(doc['bucket_name'], [])
        comparison_buckets[doc['bucket_name']].append(doc)

    # `shared_buckets` references buckets shared by both `revision_id` and
    # `comparison_revision_id` -- i.e. their intersection.
    shared_buckets = set(buckets.keys()).intersection(
        comparison_buckets.keys())
    # `unshared_buckets` references buckets not shared by both `revision_id`
    # and `comparison_revision_id` -- i.e. their non-intersection.
    unshared_buckets = set(buckets.keys()).union(
        comparison_buckets.keys()) - shared_buckets

    result = {}

    def _compare_buckets(b1, b2):
        # Checks whether buckets' documents are identical.
        return (sorted([
            (d['data_hash'], d['metadata_hash']) for d in b1
        ]) == sorted([(d['data_hash'], d['metadata_hash']) for d in b2]))

    # If the list of documents for each bucket is identical, then the result
    # is "unmodified", else "modified".
    for bucket_name in shared_buckets:
        unmodified = _compare_buckets(buckets[bucket_name],
                                      comparison_buckets[bucket_name])
        if unmodified:
            result[bucket_name] = 'unmodified'
        else:
            result[bucket_name] = 'modified'
            # If deepdiff enabled
            if deepdiff:
                # find out diff between buckets
                bucket_diff = _diff_buckets(buckets[bucket_name],
                                            comparison_buckets[bucket_name])
                result[bucket_name + ' diff'] = bucket_diff

    for bucket_name in unshared_buckets:
        # If neither revision has documents, then there's nothing to compare.
        # This is always True for revision_id == comparison_revision_id == 0.
        if not any([revision, comparison_revision]):
            break
        # Else if one revision == 0 and the other revision != 0, then the
        # bucket has been created. Which is zero or non-zero doesn't matter.
        elif not all([revision, comparison_revision]):
            result[bucket_name] = 'created'
        # Else if `revision` is newer than `comparison_revision`, then if the
        # `bucket_name` isn't in the `revision` buckets, then it has been
        # deleted. Otherwise it has been created.
        elif revision['created_at'] > comparison_revision['created_at']:
            if bucket_name not in buckets:
                result[bucket_name] = 'deleted'
            elif bucket_name not in comparison_buckets:
                result[bucket_name] = 'created'
        # Else if `comparison_revision` is newer than `revision`, then if the
        # `bucket_name` isn't in the `revision` buckets, then it has been
        # created. Otherwise it has been deleted.
        else:
            if bucket_name not in buckets:
                result[bucket_name] = 'created'
            elif bucket_name not in comparison_buckets:
                result[bucket_name] = 'deleted'

    return result