Repository: plone/plone.restapi (language: python)

Issue:
History endpoint returns internal server error when id changed
When you change the id of a content object and want to fetch a previous version (with a different id) the server responds with an internal server error:

```
2023-05-31 10:18:00,800 INFO    [plone.protect:285][waitress-3]   File "/Users/rob/.pyenv/versions/3.9.15/lib/python3.9/threading.py", line 937, in _bootstrap
    self._bootstrap_inner()

  File "/Users/rob/.pyenv/versions/3.9.15/lib/python3.9/threading.py", line 980, in _bootstrap_inner
    self.run()

  File "/Users/rob/.pyenv/versions/3.9.15/lib/python3.9/threading.py", line 917, in run
    self._target(*self._args, **self._kwargs)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/waitress/task.py", line 84, in handler_thread
    task.service()

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/waitress/channel.py", line 428, in service
    task.service()

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/waitress/task.py", line 168, in service
    self.execute()

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/waitress/task.py", line 434, in execute
    app_iter = self.channel.server.application(environ, start_response)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/paste/translogger.py", line 69, in __call__
    return self.application(environ, replacement_start_response)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/ZPublisher/httpexceptions.py", line 30, in __call__
    return self.application(environ, start_response)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/ZPublisher/WSGIPublisher.py", line 381, in publish_module
    environ['REMOTE_USER'] = user.getUserName()

  File "/Users/rob/.pyenv/versions/3.9.15/lib/python3.9/contextlib.py", line 137, in __exit__
    self.gen.throw(typ, value, traceback)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/ZPublisher/WSGIPublisher.py", line 219, in transaction_pubevents
    notify(pubevents.PubBeforeAbort(request, exc_info, retry))

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/zope/event/__init__.py", line 32, in notify
    subscriber(event)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/zope/component/event.py", line 27, in dispatch
    component_subscribers(event, None)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/zope/component/_api.py", line 134, in subscribers
    return sitemanager.subscribers(objects, interface)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/zope/interface/registry.py", line 448, in subscribers
    return self.adapters.subscribers(objects, provided)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/zope/interface/adapter.py", line 899, in subscribers
    subscription(*objects)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/transformchain/zpublisher.py", line 121, in applyTransformOnFailure
    applyTransformOnSuccess(event)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/transformchain/zpublisher.py", line 86, in applyTransformOnSuccess
    transformed = applyTransform(event.request)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/transformchain/zpublisher.py", line 75, in applyTransform
    transformed = transformer(request, result, encoding)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/transformchain/transformer.py", line 59, in __call__
    newResult = handler.transformIterable(result, encoding)

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/protect/auto.py", line 191, in transformIterable
    if not self.check():

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/protect/auto.py", line 216, in check
    return self._check()

  File "/Users/rob/Projects/dlr-internet/backend/lib/python3.9/site-packages/plone/protect/auto.py", line 288, in _check
    '\n'.join(traceback.format_stack()),

aborting transaction due to no CSRF protection on url http://localhost:3000/de/history-test-1/@history/0
2023-05-31 10:18:00,801 ERROR   [Zope.SiteErrorLog:35][waitress-3] ValueError: http://localhost:3000/de/history-test-1/@history/0
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 167, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 376, in publish_module
  Module ZPublisher.WSGIPublisher, line 271, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZPublisher.WSGIPublisher, line 68, in call_object
  Module plone.rest.service, line 22, in __call__
  Module plone.restapi.services, line 19, in render
  Module plone.restapi.services.history.get, line 29, in reply
  Module plone.restapi.serializer.dxcontent, line 160, in __call__
  Module plone.restapi.serializer.dxcontent, line 83, in __call__
  Module plone.restapi.serializer.nextprev, line 53, in previous
  Module plone.app.dexterity.behaviors.nextprevious, line 96, in getPreviousItem
ValueError: 'history-test' is not in list
```

Steps to reproduce:
- Create a page
- Edit the page and change the short name
- View the initial version of the page


Discussion / hints:

Fix patch:
diff --git a/news/1651.bugfix b/news/1651.bugfix
new file mode 100644
index 0000000000..d64b315b5c
--- /dev/null
+++ b/news/1651.bugfix
@@ -0,0 +1,1 @@
+Fix content serializer with an old version of an item that was renamed. @davisagli
diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py
index fbadaf066d..f3967e145b 100644
--- a/src/plone/restapi/serializer/dxcontent.py
+++ b/src/plone/restapi/serializer/dxcontent.py
@@ -81,10 +81,15 @@ def __call__(self, version=None, include_items=True):
         }
 
         # Insert next/prev information
-        nextprevious = NextPrevious(obj)
-        result.update(
-            {"previous_item": nextprevious.previous, "next_item": nextprevious.next}
-        )
+        try:
+            nextprevious = NextPrevious(obj)
+            result.update(
+                {"previous_item": nextprevious.previous, "next_item": nextprevious.next}
+            )
+        except ValueError:
+            # If we're serializing an old version that was renamed or moved,
+            # then its id might not be found inside the current object's container.
+            result.update({"previous_item": {}, "next_item": {}})
 
         # Insert working copy information
         if WorkingCopyInfo is not None:


Test patch:
diff --git a/src/plone/restapi/tests/test_services_history.py b/src/plone/restapi/tests/test_services_history.py
index a75eb1c58e..2ad04803de 100644
--- a/src/plone/restapi/tests/test_services_history.py
+++ b/src/plone/restapi/tests/test_services_history.py
@@ -119,6 +119,15 @@ def test_previous_version(self):
         response = self.api_session.get(url)
         self.assertEqual(response.json()["title"], "My Document")
 
+    def test_previous_version_of_renamed_item(self):
+        api.content.move(source=self.doc, id="renamed-doc")
+        transaction.commit()
+
+        url = self.doc.absolute_url() + "/@history/0"
+        response = self.api_session.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json()["id"], "doc_with_history")
+
     def test_no_sharing(self):
         url = self.doc.absolute_url() + "/@history/0"
         response = self.api_session.get(url)