Skip to content

Commit

Permalink
Fix for acquired objects moved into their parents.
Browse files Browse the repository at this point in the history
Correct special case handling for objects that were moved into one of their
parent objects and thus can still be found via acquisition when their former
path is used as their id was not changed.
We assumed that the redirector is involved, this is not correct. Thus drop
redirector check and instead make sure that the acquisition chain of the wrapped
and unwrapped object is different.
  • Loading branch information
deiferni committed Jul 2, 2019
1 parent 2985df7 commit e4f94fb
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 16 deletions.
31 changes: 15 additions & 16 deletions ftw/catalogdoctor/surgery.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from Acquisition import aq_chain
from Acquisition import aq_inner
from ftw.catalogdoctor.compat import DateRecurringIndex
from ftw.catalogdoctor.exceptions import CantPerformSurgery
from ftw.catalogdoctor.utils import find_keys_pointing_to_rid
from plone import api
from plone.app.folder.nogopip import GopipIndex
from plone.app.redirector.interfaces import IRedirectionStorage
from Products.ExtendedPathIndex.ExtendedPathIndex import ExtendedPathIndex
from Products.PluginIndexes.BooleanIndex.BooleanIndex import BooleanIndex
from Products.PluginIndexes.DateIndex.DateIndex import DateIndex
Expand All @@ -12,7 +13,6 @@
from Products.PluginIndexes.KeywordIndex.KeywordIndex import KeywordIndex
from Products.PluginIndexes.UUIDIndex.UUIDIndex import UUIDIndex
from Products.ZCTextIndex.ZCTextIndex import ZCTextIndex
from zope.component import queryUtility


class IndexSurgery(object):
Expand Down Expand Up @@ -312,12 +312,13 @@ def perform(self):
class RemoveRidOrReindexObject(Surgery):
"""Reindex an object for all indexes or remove the stray rid.
This can have three causes:
- Either there are stray rids left behind in the catalogs `uid` and `path`
mappings. In such cases the object is no longer traversable as plone
content and we can remove the stray rid.
- The object has been moved, but somehow `uid` and `path` mappings have
not been updated correctly. We can remove the stray rid.
This can have two causes:
- Either there are orphaned rids left behind in the catalogs `uid` and
`path` mappings. In such cases the referenced object is is no longer
traversable as plone content and we can remove the orphaned rid.
- Special case of above when the object has been moved into its parents. In
such cases the object can still be traversed as object is found via
acquisition. We can remove the orphaned rid in such cases.
- The object has not been indexed correctly, in such cases the object can
be traversed and has to be reindexed in all indexes.
Expand Down Expand Up @@ -359,18 +360,16 @@ def perform(self):

return

# this happens when the object has been moved but the old path has not
# been correctly removed from the catalog. we expect a redirect in such
# cases, also the path of the object we traversed to will be different
# from the path we input to `unrestrictedTraverse`
# special case when the object has been moved into one of its parents.
# it can be traversed as it is found via acquisition. safeguard so we
# only unindex objects where this special case is true.
obj_path = '/'.join(obj.getPhysicalPath())
if obj_path != path:
storage = queryUtility(IRedirectionStorage)
if storage.get(path) != obj_path:
if aq_chain(aq_inner(obj))[1:] == aq_chain(obj)[1:]:
raise CantPerformSurgery(
"Object path after traversing {} differs from path before "
"traversing and in catalog {}, but no redirect is "
"registered.".format(obj_path, path))
"traversing and in catalog {}, but acquisition chain "
"is unexpectedly equal.".format(obj_path, path))

self.unindex_rid_from_all_catalog_indexes(rid)
self.delete_rid_from_paths(rid)
Expand Down
61 changes: 61 additions & 0 deletions ftw/catalogdoctor/tests/test_surgery.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ftw.builder import Builder
from ftw.builder import create
from ftw.catalogdoctor.exceptions import CantPerformSurgery
from ftw.catalogdoctor.surgery import ReindexMissingUUID
from ftw.catalogdoctor.surgery import RemoveExtraRid
from ftw.catalogdoctor.surgery import RemoveOrphanedRid
Expand Down Expand Up @@ -133,6 +134,66 @@ def test_surgery_reindex_missing_uuid(self):
result = self.run_healthcheck()
self.assertTrue(result.is_healthy())

def test_surgery_remove_object_moved_into_parent_and_found_via_acquisition_abort(self):
self.parent['qux'] = self.child
broken_path = '/'.join(self.child.getPhysicalPath()[:-1] + ('qux',))

rid = self.choose_next_rid()
self.catalog.uids[broken_path] = rid
self.catalog.paths[rid] = broken_path
self.catalog.data[rid] = {}
self.catalog._length.change(1)

result = self.run_healthcheck()
self.assertFalse(result.is_healthy())
unhealthy_rid = result.get_unhealthy_rids()[0]
self.assertEqual(rid, unhealthy_rid.rid)
self.assertEqual(
(
'in_catalog_not_in_uuid_index',
'in_catalog_not_in_uuid_unindex',
),
result.get_symptoms(unhealthy_rid.rid))

surgery = RemoveRidOrReindexObject(self.catalog, unhealthy_rid)
with self.assertRaises(CantPerformSurgery):
surgery.perform()

def test_surgery_remove_object_moved_into_parent_and_found_via_acquisition(self):
grandchild = create(Builder('folder')
.within(self.child)
.titled(u'nastygrandchild'))

old_grandchild_path = '/'.join(grandchild.getPhysicalPath())
# move object into parent's parent
self.parent.manage_pasteObjects(
self.child.manage_cutObjects(grandchild.getId()),
)

# re-register old grandchild path with different rid
rid = self.choose_next_rid()
self.catalog.uids[old_grandchild_path] = rid
self.catalog.paths[rid] = old_grandchild_path
self.catalog.data[rid] = {}
self.catalog._length.change(1)

result = self.run_healthcheck()
self.assertFalse(result.is_healthy())
unhealthy_rid = result.get_unhealthy_rids()[0]
self.assertEqual(rid, unhealthy_rid.rid)
self.assertEqual(
(
'in_catalog_not_in_uuid_index',
'in_catalog_not_in_uuid_unindex',
),
result.get_symptoms(unhealthy_rid.rid))

surgery = RemoveRidOrReindexObject(self.catalog, unhealthy_rid)
surgery.perform()

result = self.run_healthcheck()
self.assertTrue(result.is_healthy())

def test_surgery_add_dropped_object_to_indices(self):
self.drop_object_from_catalog_indexes(self.parent)

Expand Down

0 comments on commit e4f94fb

Please sign in to comment.