mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-08-19 09:30:21 +00:00
113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
"""Builders for the JSON:API documents the Prowler API returns.
|
|
|
|
Every model's ``from_api_response()`` and every tool's error path consumes one of
|
|
these shapes, so building them by hand in each test would duplicate the document
|
|
structure hundreds of times. The builders keep the *shape* in one place so tests
|
|
only express the part they actually care about.
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
|
|
def jsonapi_relationship_many(resource_type: str, *ids: str) -> dict[str, Any]:
|
|
"""Build a to-many relationship.
|
|
|
|
Passing no ids yields a present-but-empty relationship (``{"data": []}``),
|
|
which ``extract_relationship_ids`` reports as ``[]`` rather than ``None``.
|
|
"""
|
|
return {"data": [{"type": resource_type, "id": resource_id} for resource_id in ids]}
|
|
|
|
|
|
def jsonapi_relationship_one(resource_type: str, resource_id: str) -> dict[str, Any]:
|
|
"""Build a to-one relationship."""
|
|
return {"data": {"type": resource_type, "id": resource_id}}
|
|
|
|
|
|
def jsonapi_resource(
|
|
resource_type: str,
|
|
resource_id: str,
|
|
attributes: dict[str, Any] | None = None,
|
|
relationships: dict[str, Any] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Build a single JSON:API resource object.
|
|
|
|
``relationships`` is omitted from the result entirely when not supplied, so a
|
|
test can express "the document did not expose this relationship"
|
|
(``extract_relationship_ids`` -> ``None``) distinctly from "the relationship
|
|
is present and empty" (-> ``[]``). Conflating the two is exactly the bug the
|
|
models go out of their way to avoid.
|
|
"""
|
|
resource: dict[str, Any] = {
|
|
"type": resource_type,
|
|
"id": resource_id,
|
|
"attributes": attributes or {},
|
|
}
|
|
if relationships is not None:
|
|
resource["relationships"] = relationships
|
|
return resource
|
|
|
|
|
|
def jsonapi_document(
|
|
data: dict[str, Any] | list[dict[str, Any]],
|
|
included: list[dict[str, Any]] | None = None,
|
|
meta: dict[str, Any] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Build a top-level JSON:API document."""
|
|
document: dict[str, Any] = {"data": data}
|
|
if included is not None:
|
|
document["included"] = included
|
|
if meta is not None:
|
|
document["meta"] = meta
|
|
return document
|
|
|
|
|
|
def jsonapi_collection(
|
|
items: list[dict[str, Any]],
|
|
*,
|
|
page: int = 1,
|
|
pages: int = 1,
|
|
count: int | None = None,
|
|
included: list[dict[str, Any]] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Build a paginated collection document.
|
|
|
|
The ``meta.pagination`` keys are exactly the ones every ``*ListResponse``
|
|
reads (``page``, ``pages``, ``count``). ``count`` defaults to the number of
|
|
items so the common single-page case needs no arguments.
|
|
"""
|
|
return jsonapi_document(
|
|
data=items,
|
|
included=included,
|
|
meta={
|
|
"pagination": {
|
|
"page": page,
|
|
"pages": pages,
|
|
"count": len(items) if count is None else count,
|
|
}
|
|
},
|
|
)
|
|
|
|
|
|
def jsonapi_error(status: int, detail: str, title: str | None = None) -> dict[str, Any]:
|
|
"""Build an error document.
|
|
|
|
``ProwlerAPIClient._make_request`` surfaces ``errors[0].detail`` in the
|
|
exception message it raises, and tools relay that straight to the model.
|
|
"""
|
|
error: dict[str, Any] = {"status": str(status), "detail": detail}
|
|
if title is not None:
|
|
error["title"] = title
|
|
return {"errors": [error]}
|
|
|
|
|
|
def task_document(task_id: str, state: str, error: str | None = None) -> dict[str, Any]:
|
|
"""Build a ``/tasks/{id}`` document for driving ``poll_task_until_complete``.
|
|
|
|
Register a sequence of these on a ``MockRouter`` route (for example
|
|
``executing``, ``executing``, ``completed``) to exercise the polling loop.
|
|
"""
|
|
attributes: dict[str, Any] = {"state": state}
|
|
if error is not None:
|
|
attributes["error"] = error
|
|
return jsonapi_document(jsonapi_resource("tasks", task_id, attributes))
|