Implementation:MarketSquare Robotframework browser Format Response: Difference between revisions
Auto-imported from implementations/MarketSquare_Robotframework_browser_Format_Response.md |
Sync from local file |
||
| Line 230: | Line 230: | ||
=== Related === | === Related === | ||
* [[MarketSquare_Robotframework_browser_Response_Formatting_and_Validation|Response Formatting and Validation (Principle)]] | * [[Principle:MarketSquare_Robotframework_browser_Response_Formatting_and_Validation|Response Formatting and Validation (Principle)]] | ||
* [[MarketSquare_Robotframework_browser_Network_Http|Network Http (Implementation)]] | * [[Implementation:MarketSquare_Robotframework_browser_Network_Http|Network Http (Implementation)]] | ||
* [[MarketSquare_Robotframework_browser_Wait_For_Response_Wait_For_Request|Wait For Response / Wait For Request]] | * [[Implementation:MarketSquare_Robotframework_browser_Wait_For_Response_Wait_For_Request|Wait For Response / Wait For Request]] | ||
[[Category:Implementations]] | [[Category:Implementations]] | ||
Latest revision as of 10:45, 27 September 2026
Format Response
API documentation for the internal helper functions that transform raw HTTP response data into structured Python objects.
Overview
The response formatting helpers are module-level functions defined at the top of Browser/keywords/network.py. They are not Robot Framework keywords -- they are internal Python functions used by the HTTP, Wait For Response, and related keywords to process raw gRPC response data into structured, queryable dictionaries. These functions handle JSON auto-parsing of response bodies, header string-to-dictionary conversion, and Content-Type detection.
Source Location
| Function | File | Lines |
|---|---|---|
_get_headers |
Browser/keywords/network.py |
L26-31 |
_format_response |
Browser/keywords/network.py |
L34-40 |
_jsonize_content |
Browser/keywords/network.py |
L43-54 |
Function: _get_headers
Signature
def _get_headers(body: str, headers: dict):
Purpose
Determines whether the request body is JSON and, if so, automatically adds a Content-Type: application/json header. This function is called before sending an HTTP request (used by the http keyword).
Implementation
def _get_headers(body: str, headers: dict):
try:
json.loads(body)
return {"Content-Type": "application/json", **headers}
except json.decoder.JSONDecodeError:
return headers
Behavior
- Attempts to parse the
bodystring as JSON usingjson.loads(). - If parsing succeeds: Returns a new dictionary with
Content-Type: application/jsonmerged with the caller-provided headers. The caller's headers take precedence due to the**headersspread appearing after the Content-Type entry (if the caller explicitly sets Content-Type, it overrides the auto-detected value). - If parsing fails (raises
json.decoder.JSONDecodeError): Returns the caller-provided headers unchanged.
Parameters
| Parameter | Type | Description |
|---|---|---|
body |
str |
The request body string to test for JSON validity. |
headers |
dict |
The caller-provided request headers dictionary. |
Returns
A dictionary of headers, potentially with Content-Type: application/json prepended.
Function: _format_response
Signature
def _format_response(response: dict) -> dict:
Purpose
Transforms a raw response dictionary (deserialized from gRPC JSON) into a properly structured dictionary with parsed headers and auto-parsed JSON bodies. This is the main entry point for response formatting.
Implementation
def _format_response(response: dict) -> dict:
_jsonize_content(response, "body")
if "request" in response:
request = response["request"]
_jsonize_content(request, "postData")
logger.info(response)
return response
Behavior
- Calls
_jsonize_content(response, "body")to parse response headers and conditionally parse the response body as JSON. - If the response dictionary contains a
requestsub-dictionary (present inWait For Responseresults), calls_jsonize_content(request, "postData")to parse request headers and conditionally parse the request's post data as JSON. - Logs the formatted response at INFO level.
- Returns the modified response dictionary (the dictionary is mutated in place by
_jsonize_content).
Parameters
| Parameter | Type | Description |
|---|---|---|
response |
dict |
The raw response dictionary, typically produced by json.loads() on the gRPC response JSON.
|
Returns
The same dictionary, modified in place with parsed headers and optionally parsed body/postData.
Function: _jsonize_content
Signature
def _jsonize_content(data, bodykey):
Purpose
Converts stringified headers to a dictionary and conditionally parses a body field (identified by bodykey) from JSON string to Python dictionary, based on the Content-Type header.
Implementation
def _jsonize_content(data, bodykey):
headers = json.loads(data.get("headers", "{}"))
data["headers"] = headers
lower_headers = {k.lower(): v for k, v in headers.items()}
if (
"content-type" in lower_headers
and "application/json" in lower_headers["content-type"]
and bodykey in data
and data[bodykey]
):
with contextlib.suppress(json.decoder.JSONDecodeError, TypeError):
data[bodykey] = json.loads(data[bodykey])
Behavior
- Header parsing: Parses the
headersfield from a JSON string to a Python dictionary. Ifheadersis missing, defaults to an empty dictionary ("{}"). - Case-insensitive Content-Type check: Creates a lowercase copy of all header keys to perform a case-insensitive lookup for
content-type. - Conditional body parsing: If all of the following conditions are true, the body field is parsed as JSON:
- The headers contain a
content-typekey (case-insensitive). - The content-type value contains the substring
application/json. - The
bodykeyexists in the data dictionary. - The body value is truthy (not None, not empty string).
- The headers contain a
- Error suppression: If JSON parsing fails (
JSONDecodeError) or encounters a type error (TypeError), the error is silently suppressed and the body remains as its original string value.
Parameters
| Parameter | Type | Description |
|---|---|---|
data |
dict |
The dictionary to process (either the response dict or the nested request dict). |
bodykey |
str |
The key name of the body field to conditionally parse. Typically "body" for responses or "postData" for requests.
|
Returns
None. The function modifies the data dictionary in place.
How These Functions Are Used
In the HTTP keyword (L104):
# Before sending the request:
headers=json.dumps(_get_headers(body, headers))
# After receiving the response:
response_dict = _format_response(json.loads(response.json))
return DotDict(response_dict)
In _wait_for_http_response (L134-140):
response_json = json.loads(responce.json)
# Body may have been streamed in chunks:
if response_json.get("body", not_none) is not None and body:
response_json["body"] = body
return _format_response(response_json)
Data Flow Diagram
The complete data flow from Node.js to the Robot Framework test:
Node.js (fetch/waitForResponse)
|
v
gRPC Response (JSON string)
| - headers: '{"content-type": "application/json"}'
| - body: '{"users": [{"id": 1, "name": "John"}]}'
| - status: 200
|
v
json.loads() -> Python dict
| - headers: '{"content-type": "application/json"}' (still a string)
| - body: '{"users": [{"id": 1, "name": "John"}]}' (still a string)
|
v
_format_response() -> _jsonize_content()
| - headers: {"content-type": "application/json"} (now a dict)
| - body: {"users": [{"id": 1, "name": "John"}]} (now a dict)
|
v
DotDict() wrapping
| - ${response.headers} -> dict
| - ${response.body.users} -> list
| - ${response.body.users[0].name} -> "John"
|
v
Robot Framework test assertions
Important Notes
- These functions are module-level (not methods on the
Networkclass), so they do not have access toselfor any library state. - The
_format_responsefunction mutates its input dictionary rather than creating a copy. This is intentional for efficiency. - The
_get_headersfunction is only used for outgoing requests (theHTTPkeyword), while_format_responseand_jsonize_contentare used for incoming response processing. - The Content-Type check uses
in(substring match), so content types likeapplication/json; charset=utf-8are correctly detected.