Пример #1
0
    def test_softdelete_datalayer(self):
        """Soft deleted items should not be returned by find methods in the Eve
        data layer unless show_deleted is explicitly configured in the request,
        the deleted field is included in the lookup, or the operation is 'raw'.
        """
        # Soft delete item
        r, status = self.delete(self.item_id_url, headers=self.etag_headers)
        self.assert204(status)

        with self.app.test_request_context():
            # find_one should only return item if a request w/ show_deleted ==
            # True is passed or if the deleted field is part of the lookup
            req = ParsedRequest()
            doc = self.app.data.find_one(self.known_resource,
                                         req,
                                         _id=self.item_id)
            self.assertEqual(doc, None)

            req.show_deleted = True
            doc = self.app.data.find_one(self.known_resource,
                                         req,
                                         _id=self.item_id)
            self.assertNotEqual(doc, None)
            self.assertEqual(doc.get(self.deleted_field), True)

            req.show_deleted = False
            doc = self.app.data.find_one(self.known_resource,
                                         req,
                                         _id=self.item_id,
                                         _deleted=True)
            self.assertNotEqual(doc, None)
            self.assertEqual(doc.get(self.deleted_field), True)

            # find_one_raw should always return a document, soft deleted or not
            doc = self.app.data.find_one_raw(self.known_resource,
                                             _id=ObjectId(self.item_id))
            self.assertNotEqual(doc, None)
            self.assertEqual(doc.get(self.deleted_field), True)

            # find should only return deleted items if a request with
            # show_deleted == True is passed or if the deleted field is part of
            # the lookup
            req.show_deleted = False
            _, undeleted_count = self.app.data.find(self.known_resource, req,
                                                    None)

            req.show_deleted = True
            _, challenge = self.app.data.find(self.known_resource, req, None)
            self.assertEqual(undeleted_count, challenge - 1)

            req.show_deleted = False
            _, deleted_count = self.app.data.find(self.known_resource, req,
                                                  {self.deleted_field: True})
            self.assertEqual(deleted_count, 1)

            # find_list_of_ids will return deleted documents if given their id
            ids = self.app.data.find_list_of_ids(self.known_resource,
                                                 [ObjectId(self.item_id)])

            self.assertEqual(str(ids[0]["_id"]), self.item_id)
Пример #2
0
    def test_softdelete_datalayer(self):
        """Soft deleted items should not be returned by find methods in the Eve
        data layer unless show_deleted is explicitly configured in the request,
        the deleted field is included in the lookup, or the operation is 'raw'.
        """
        # Soft delete item
        r, status = self.delete(self.item_id_url, headers=self.etag_headers)
        self.assert204(status)

        with self.app.test_request_context():
            # find_one should only return item if a request w/ show_deleted ==
            # True is passed or if the deleted field is part of the lookup
            req = ParsedRequest()
            doc = self.app.data.find_one(
                self.known_resource, req, _id=self.item_id)
            self.assertEqual(doc, None)

            req.show_deleted = True
            doc = self.app.data.find_one(
                self.known_resource, req, _id=self.item_id)
            self.assertNotEqual(doc, None)
            self.assertEqual(doc.get(self.deleted_field), True)

            req.show_deleted = False
            doc = self.app.data.find_one(
                self.known_resource, req, _id=self.item_id, _deleted=True)
            self.assertNotEqual(doc, None)
            self.assertEqual(doc.get(self.deleted_field), True)

            # find_one_raw should always return a document, soft deleted or not
            doc = self.app.data.find_one_raw(
                self.known_resource, _id=ObjectId(self.item_id))
            self.assertNotEqual(doc, None)
            self.assertEqual(doc.get(self.deleted_field), True)

            # find should only return deleted items if a request with
            # show_deleted == True is passed or if the deleted field is part of
            # the lookup
            req.show_deleted = False
            docs = self.app.data.find(self.known_resource, req, None)
            undeleted_count = docs.count()

            req.show_deleted = True
            docs = self.app.data.find(self.known_resource, req, None)
            with_deleted_count = docs.count()
            self.assertEqual(undeleted_count, with_deleted_count - 1)

            req.show_deleted = False
            docs = self.app.data.find(
                self.known_resource, req, {self.deleted_field: True})
            deleted_count = docs.count()
            self.assertEqual(deleted_count, 1)

            # find_list_of_ids will return deleted documents if given their id
            docs = self.app.data.find_list_of_ids(
                self.known_resource, [ObjectId(self.item_id)])
            self.assertEqual(docs.count(), 1)
Пример #3
0
def delete(resource, **lookup):
    """ Deletes all item of a resource (collection in MongoDB terms). Won't
    drop indexes. Use with caution!

    .. versionchanged:: 0.5
       Return 204 NoContent instead of 200.

    .. versionchanged:: 0.4
       Support for document versioning.
       'on_delete_resource' raised before performing the actual delete.
       'on_deleted_resource' raised after performing the delete

    .. versionchanged:: 0.3
       Support for the lookup filter, which allows for develtion of
       sub-resources (only delete documents that match a given condition).

    .. versionchanged:: 0.0.4
       Added the ``requires_auth`` decorator.

    .. versionadded:: 0.0.2
    """

    resource_def = config.DOMAIN[resource]
    getattr(app, "on_delete_resource")(resource)
    getattr(app, "on_delete_resource_%s" % resource)()
    default_request = ParsedRequest()
    if resource_def["soft_delete"]:
        # get_document should always fetch soft deleted documents from the db
        # callers must handle soft deleted documents
        default_request.show_deleted = True
    result, _ = app.data.find(resource, default_request, lookup)
    originals = list(result)
    if not originals:
        return all_done()
    # I add new callback as I want the framework to be retro-compatible
    getattr(app, "on_delete_resource_originals")(resource, originals, lookup)
    getattr(app, "on_delete_resource_originals_%s" % resource)(originals,
                                                               lookup)
    id_field = resource_def["id_field"]

    if resource_def["soft_delete"]:
        # I need to check that I have at least some documents not soft_deleted
        # I skip all the soft_deleted documents
        originals = [x for x in originals if x.get(config.DELETED) is False]
        if not originals:
            # Nothing to be deleted
            return all_done()
        for document in originals:
            lookup[id_field] = document[id_field]
            deleteitem_internal(resource,
                                concurrency_check=False,
                                suppress_callbacks=True,
                                original=document,
                                **lookup)
    else:
        # TODO if the resource schema includes media files, these won't be
        # deleted by use of this global method (it should be disabled). Media
        # cleanup is handled at the item endpoint by the delete() method
        # (see above).
        app.data.remove(resource, lookup)

        # TODO: should attempt to delete version collection even if setting is
        # off
        if resource_def["versioning"] is True:
            app.data.remove(resource + config.VERSIONS, lookup)

    getattr(app, "on_deleted_resource")(resource)
    getattr(app, "on_deleted_resource_%s" % resource)()

    return all_done()
Пример #4
0
def get_data_version_relation_document(data_relation, reference, latest=False):
    """ Returns document at the version specified in data_relation, or at the
    latest version if passed `latest=True`. Returns None if data_relation
    cannot be satisfied.

    :param data_relation: the schema definition describing the data_relation.
    :param reference: a dictionary with a value_field and a version_field.
    :param latest: if we should obey the version param in reference or not.

    .. versionadded:: 0.4
    """
    value_field = data_relation['field']
    version_field = app.config['VERSION']
    collection = data_relation['resource']
    versioned_collection = collection + config.VERSIONS
    resource_def = app.config['DOMAIN'][data_relation['resource']]
    id_field = app.config['ID_FIELD']

    # Fetch document data at the referenced version
    query = {version_field: reference[version_field]}
    if value_field == id_field:
        # Versioned documents store the primary id in a different field
        query[versioned_id_field()] = reference[value_field]
    elif value_field not in versioned_fields(resource_def):
        # The relation value field is unversioned, and will not be present in
        # the versioned collection. Need to find id field for version query
        req = ParsedRequest()
        if resource_def['soft_delete']:
            req.show_deleted = True
        latest_version = app.data.find_one(
            collection, req, **{value_field: reference[value_field]})
        if not latest_version:
            return None
        query[versioned_id_field()] = latest_version[id_field]
    else:
        # Field will be present in the versioned collection
        query[value_field] = reference[value_field]

    referenced_version = app.data.find_one(versioned_collection, None, **query)

    # support late versioning
    if referenced_version is None and reference[version_field] == 1:
        # there is a chance this document hasn't been saved
        # since versioning was turned on
        referenced_version = missing_version_field(data_relation, reference)
        return referenced_version  # v1 is both referenced and latest

    if referenced_version is None:
        return None  # The referenced document version was not found

    # Fetch the latest version of this document to use in version synthesis
    query = {id_field: referenced_version[versioned_id_field()]}
    req = ParsedRequest()
    if resource_def['soft_delete']:
        # Still return latest after soft delete. It is needed to synthesize
        # full document version.
        req.show_deleted = True
    latest_version = app.data.find_one(collection, req, **query)
    if latest is True:
        return latest_version

    # Syntheisze referenced version from latest and versioned data
    document = synthesize_versioned_document(
        latest_version, referenced_version, resource_def)
    return document
Пример #5
0
def get_data_version_relation_document(data_relation, reference, latest=False):
    """ Returns document at the version specified in data_relation, or at the
    latest version if passed `latest=True`. Returns None if data_relation
    cannot be satisfied.

    :param data_relation: the schema definition describing the data_relation.
    :param reference: a dictionary with a value_field and a version_field.
    :param latest: if we should obey the version param in reference or not.

    .. versionadded:: 0.4
    """
    value_field = data_relation["field"]
    version_field = app.config["VERSION"]
    collection = data_relation["resource"]
    versioned_collection = collection + config.VERSIONS
    resource_def = app.config["DOMAIN"][data_relation["resource"]]
    id_field = resource_def["id_field"]

    # Fetch document data at the referenced version
    query = {version_field: reference[version_field]}
    if value_field == id_field:
        # Versioned documents store the primary id in a different field
        query[versioned_id_field(resource_def)] = reference[value_field]
    elif value_field not in versioned_fields(resource_def):
        # The relation value field is unversioned, and will not be present in
        # the versioned collection. Need to find id field for version query
        req = ParsedRequest()
        if resource_def["soft_delete"]:
            req.show_deleted = True
        latest_version = app.data.find_one(
            collection, req, **{value_field: reference[value_field]})
        if not latest_version:
            return None
        query[versioned_id_field(resource_def)] = latest_version[id_field]
    else:
        # Field will be present in the versioned collection
        query[value_field] = reference[value_field]

    referenced_version = app.data.find_one(versioned_collection, None, **query)

    # support late versioning
    if referenced_version is None and reference[version_field] == 1:
        # there is a chance this document hasn't been saved
        # since versioning was turned on
        referenced_version = missing_version_field(data_relation, reference)
        return referenced_version  # v1 is both referenced and latest

    if referenced_version is None:
        return None  # The referenced document version was not found

    # Fetch the latest version of this document to use in version synthesis
    query = {id_field: referenced_version[versioned_id_field(resource_def)]}
    req = ParsedRequest()
    if resource_def["soft_delete"]:
        # Still return latest after soft delete. It is needed to synthesize
        # full document version.
        req.show_deleted = True
    latest_version = app.data.find_one(collection, req, **query)
    if latest is True:
        return latest_version

    # Syntheisze referenced version from latest and versioned data
    document = synthesize_versioned_document(latest_version,
                                             referenced_version, resource_def)
    return document
Пример #6
0
def delete(resource, **lookup):
    """ Deletes all item of a resource (collection in MongoDB terms). Won't
    drop indexes. Use with caution!

    .. versionchanged:: 0.5
       Return 204 NoContent instead of 200.

    .. versionchanged:: 0.4
       Support for document versioning.
       'on_delete_resource' raised before performing the actual delete.
       'on_deleted_resource' raised after performing the delete

    .. versionchanged:: 0.3
       Support for the lookup filter, which allows for develtion of
       sub-resources (only delete documents that match a given condition).

    .. versionchanged:: 0.0.4
       Added the ``requires_auth`` decorator.

    .. versionadded:: 0.0.2
    """

    resource_def = config.DOMAIN[resource]
    getattr(app, "on_delete_resource")(resource)
    getattr(app, "on_delete_resource_%s" % resource)()
    default_request = ParsedRequest()
    if resource_def["soft_delete"]:
        # get_document should always fetch soft deleted documents from the db
        # callers must handle soft deleted documents
        default_request.show_deleted = True
    originals = list(app.data.find(resource, default_request, lookup))
    if not originals:
        abort(404)
    # I add new callback as I want the framework to be retro-compatible
    getattr(app, "on_delete_resource_originals")(resource, originals, lookup)
    getattr(app, "on_delete_resource_originals_%s" % resource)(originals, lookup)
    id_field = resource_def["id_field"]

    if resource_def["soft_delete"]:
        # I need to check that I have at least some documents not soft_deleted
        # Otherwise, I should abort 404
        # I skip all the soft_deleted documents
        originals = [x for x in originals if x.get(config.DELETED) is not True]
        if not originals:
            # Nothing to be deleted
            abort(404)
        for document in originals:
            lookup[id_field] = document[id_field]
            deleteitem_internal(
                resource,
                concurrency_check=False,
                suppress_callbacks=True,
                original=document,
                **lookup
            )
    else:
        # TODO if the resource schema includes media files, these won't be
        # deleted by use of this global method (it should be disabled). Media
        # cleanup is handled at the item endpoint by the delete() method
        # (see above).
        app.data.remove(resource, lookup)

        # TODO: should attempt to delete version collection even if setting is
        # off
        if resource_def["versioning"] is True:
            app.data.remove(resource + config.VERSIONS, lookup)

    getattr(app, "on_deleted_resource")(resource)
    getattr(app, "on_deleted_resource_%s" % resource)()

    return {}, None, None, 204