Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions mpt_api_client/http/mixins/queryable_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
)
)

Expand All @@ -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]
)
)

Expand All @@ -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,
),
)

Expand Down
13 changes: 13 additions & 0 deletions mpt_api_client/http/query_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@ 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.

Args:
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:
Expand All @@ -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.

Expand All @@ -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 ""
12 changes: 12 additions & 0 deletions tests/e2e/audit/records/test_async_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
13 changes: 13 additions & 0 deletions tests/e2e/audit/records/test_sync_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
9 changes: 9 additions & 0 deletions tests/unit/http/mixins/test_queryable_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
19 changes: 19 additions & 0 deletions tests/unit/http/test_query_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()

Expand Down
Loading