diff --git a/mpt_api_client/http/mixins/queryable_mixin.py b/mpt_api_client/http/mixins/queryable_mixin.py index fae7b4cc..8b8f894c 100644 --- a/mpt_api_client/http/mixins/queryable_mixin.py +++ b/mpt_api_client/http/mixins/queryable_mixin.py @@ -23,6 +23,7 @@ def order_by(self, *fields: str) -> Self: rql=self.query_state.filter, # type: ignore[attr-defined] order_by=list(fields), select=self.query_state.select, # type: ignore[attr-defined] + render=self.query_state.render, # type: ignore[attr-defined] ) ) @@ -39,6 +40,7 @@ def filter(self, rql: RQLQuery) -> Self: rql=combined_filter, order_by=self.query_state.order_by, # type: ignore[attr-defined] select=self.query_state.select, # type: ignore[attr-defined] + render=self.query_state.render, # type: ignore[attr-defined] ) ) @@ -60,6 +62,22 @@ def select(self, *fields: str) -> Self: rql=self.query_state.filter, # type: ignore[attr-defined] order_by=self.query_state.order_by, # type: ignore[attr-defined] select=list(fields), + render=self.query_state.render, # type: ignore[attr-defined] + ), + ) + + def options(self, *, render: bool = False) -> Self: + """Set query options. + + Returns: + New copy of the collection with the given options applied. + """ + return self._create_new_instance( + QueryState( + rql=self.query_state.filter, # type: ignore[attr-defined] + order_by=self.query_state.order_by, # type: ignore[attr-defined] + select=self.query_state.select, # type: ignore[attr-defined] + render=render, ), ) diff --git a/mpt_api_client/http/query_state.py b/mpt_api_client/http/query_state.py index 04c530e5..80a47f81 100644 --- a/mpt_api_client/http/query_state.py +++ b/mpt_api_client/http/query_state.py @@ -17,6 +17,8 @@ def __init__( rql: RQLQuery | None = None, order_by: list[str] | None = None, select: list[str] | None = None, + *, + render: bool = False, ) -> None: """Initialize the query state with optional filter, ordering, and selection criteria. @@ -24,10 +26,12 @@ def __init__( rql: RQL query for filtering data. order_by: List of fields to order by (prefix with '-' for descending). select: List of fields to select in the response. + render: Whether to include the render() parameter in the query string. """ self._filter = rql self._order_by = order_by self._select = select + self._render = render @property def filter(self) -> RQLQuery | None: @@ -44,6 +48,11 @@ def select(self) -> list[str] | None: """Get the current select fields.""" return self._select + @property + def render(self) -> bool: + """Get the current render state.""" + return self._render + def build(self, query_params: dict[str, Any] | None = None) -> str: """Build a query string from the current state and additional parameters. @@ -66,7 +75,11 @@ def build(self, query_params: dict[str, Any] | None = None) -> str: if self._filter: query_parts.append(str(self._filter)) + if self._render: + query_parts.append("render()") + if query_parts: query = "&".join(query_parts) return f"{query}" + return "" diff --git a/tests/e2e/audit/records/test_async_records.py b/tests/e2e/audit/records/test_async_records.py index 1d450144..f87baba2 100644 --- a/tests/e2e/audit/records/test_async_records.py +++ b/tests/e2e/audit/records/test_async_records.py @@ -42,3 +42,15 @@ async def test_get_record_not_found(async_mpt_vendor: AsyncMPTClient) -> None: with pytest.raises(MPTAPIError): await service.get("REC-000-000-000") + + +async def test_get_records_with_render(async_mpt_vendor: AsyncMPTClient, product_id: str) -> None: + template_chars = ["{{", "}}"] + audit_filter = RQLQuery(object__id=product_id) + service = async_mpt_vendor.audit.records.filter(audit_filter).options(render=True) + records = [record async for record in service.iterate()] + + assert records + for record in records: + assert record.object.id == product_id + assert not any(char in record.details for char in template_chars) diff --git a/tests/e2e/audit/records/test_sync_records.py b/tests/e2e/audit/records/test_sync_records.py index 566ecc53..5b40027d 100644 --- a/tests/e2e/audit/records/test_sync_records.py +++ b/tests/e2e/audit/records/test_sync_records.py @@ -43,3 +43,16 @@ def test_get_record_not_found(mpt_vendor: MPTClient) -> None: with pytest.raises(MPTAPIError): service.get("REC-000-000-000") + + +def test_get_records_with_render(mpt_vendor: MPTClient, product_id) -> None: + template_chars = ["{{", "}}"] + audit_filter = RQLQuery(object__id=product_id) + service = mpt_vendor.audit.records.filter(audit_filter).options(render=True) + + result = list(service.iterate()) + + assert result + for record in result: + assert record.object.id == product_id + assert not any(char in record.details for char in template_chars) diff --git a/tests/unit/http/mixins/test_queryable_mixin.py b/tests/unit/http/mixins/test_queryable_mixin.py index 117a4b0f..bf66b1b4 100644 --- a/tests/unit/http/mixins/test_queryable_mixin.py +++ b/tests/unit/http/mixins/test_queryable_mixin.py @@ -78,3 +78,12 @@ def test_queryable_mixin_method_chaining( assert result.query_state.filter == filter_status_active assert result.query_state.order_by == ["created", "-name"] assert result.query_state.select == ["id", "name"] + + +def test_queryable_mixin_options_render(dummy_service: DummyService) -> None: + result = dummy_service.options(render=True) + + assert result != dummy_service + assert not dummy_service.query_state.render + assert result.query_state.render + assert result.select("id").query_state.render diff --git a/tests/unit/http/test_query_state.py b/tests/unit/http/test_query_state.py index 210029be..6c922971 100644 --- a/tests/unit/http/test_query_state.py +++ b/tests/unit/http/test_query_state.py @@ -23,6 +23,7 @@ def test_build_url(filter_status_active): rql=filter_status_active, select=["-audit", "product.agreements", "-product.agreements.product"], order_by=["-created", "name"], + render=False, ) result = query_state.build() @@ -34,6 +35,24 @@ def test_build_url(filter_status_active): ) +def test_build_url_with_render(filter_status_active): + query_state = QueryState( + rql=filter_status_active, + select=["-audit", "product.agreements", "-product.agreements.product"], + order_by=["-created", "name"], + render=True, + ) + + result = query_state.build() + + assert result == ( + "order=-created,name" + "&select=-audit,product.agreements,-product.agreements.product" + "&eq(status,'active')" + "&render()" + ) + + def test_empty_build(query_state): result = query_state.build()