From 463c55afc1467ab4d07570d1ce13404c2017778c Mon Sep 17 00:00:00 2001 From: qiaobochi040726-source Date: Fri, 23 Oct 2026 20:22:48 +0800 Subject: [PATCH] server: Omit XML cursor attribute on the last page --- server/app/interfaces/base.py | 2 +- .../test/interfaces/test_xml_paging_cursor.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 server/test/interfaces/test_xml_paging_cursor.py diff --git a/server/app/interfaces/base.py b/server/app/interfaces/base.py index ac974cbad..927dfafc0 100644 --- a/server/app/interfaces/base.py +++ b/server/app/interfaces/base.py @@ -129,7 +129,7 @@ def __init__(self, *args, content_type="application/xml", **kwargs): def serialize(self, obj: ResponseData, paging_metadata: Optional[PagingMetadata], stripped: bool) -> str: root_elem = etree.Element("response", nsmap=XML_NS_MAP) - if paging_metadata is not None: + if paging_metadata is not None and paging_metadata.cursor is not None: root_elem.set("cursor", str(paging_metadata.cursor)) if isinstance(obj, Result): result_elem = self.result_to_xml(obj, **XML_NS_MAP) diff --git a/server/test/interfaces/test_xml_paging_cursor.py b/server/test/interfaces/test_xml_paging_cursor.py new file mode 100644 index 000000000..7cd19c7ef --- /dev/null +++ b/server/test/interfaces/test_xml_paging_cursor.py @@ -0,0 +1,34 @@ +# Copyright (c) 2026 the Eclipse BaSyx Authors +# +# This program and the accompanying materials are made available under the terms of the MIT License, available in +# the LICENSE file of this project. +# +# SPDX-License-Identifier: MIT + +import unittest + +from app._config import API_BASE_PATH as BASE_PATH +from app.interfaces.repository import WSGIApp +from basyx.aas.adapter.aasx import DictSupplementaryFileContainer +from basyx.aas.examples.data.example_aas import create_full_example +from lxml import etree +from werkzeug.test import Client + + +class XmlPagingCursorTest(unittest.TestCase): + def setUp(self) -> None: + self.example_data = create_full_example() + app = WSGIApp(self.example_data, DictSupplementaryFileContainer()) + self.client = Client(app) + + def test_last_page_omits_cursor(self) -> None: + response = self.client.get(f"{BASE_PATH}/submodels?limit=100", headers={"Accept": "application/xml"}) + self.assertEqual(200, response.status_code) + root = etree.fromstring(response.data) + self.assertIsNone(root.get("cursor")) + + def test_next_page_has_cursor(self) -> None: + response = self.client.get(f"{BASE_PATH}/submodels?limit=1", headers={"Accept": "application/xml"}) + self.assertEqual(200, response.status_code) + root = etree.fromstring(response.data) + self.assertEqual("2", root.get("cursor"))