Skip to content

Commit d300d30

Browse files
committed
fix(fdc): Add custom QueryError exception and GraphQL error checking helper
- Introduced QueryError subclass of FirebaseError for Data Connect GraphQL query/mutation errors and exposed it in __all__. - Extracted _check_graphql_errors helper method on _DataConnectApiClient. - Updated error handling for non-dictionary response payloads in _parse_graphql_response to raise InternalError. - Note: Did not edit parse_graphql_response because we are waiting on whether this will even be a function or not.
1 parent 168105d commit d300d30

2 files changed

Lines changed: 44 additions & 24 deletions

File tree

firebase_admin/dataconnect.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
'GraphqlOptions',
4242
'Impersonation',
4343
'ExecuteGraphqlResponse',
44+
'QueryError',
4445
]
4546

4647
_DATA_CONNECT_ATTRIBUTE = '_data_connect'
@@ -61,6 +62,21 @@
6162
_Data = TypeVar("_Data")
6263
_Variables = TypeVar("_Variables")
6364

65+
66+
# Error Codes
67+
_QUERY_ERROR_CODE = 'query-error'
68+
69+
70+
class QueryError(exceptions.FirebaseError):
71+
"""Raised when a GraphQL query or mutation execution fails."""
72+
73+
def __init__(self, message: str, http_response: Any = None) -> None:
74+
super().__init__(
75+
code=_QUERY_ERROR_CODE,
76+
message=message,
77+
http_response=http_response
78+
)
79+
6480
@dataclass(frozen=True)
6581
class ConnectorConfig:
6682
"""A configuration object for DataConnect.
@@ -344,6 +360,30 @@ def _get_headers(self) -> Dict[str, str]:
344360
"x-goog-api-client": _utils.get_metrics_header(),
345361
}
346362

363+
@staticmethod
364+
def _check_graphql_errors(resp_dict: Any, resp: Any) -> None:
365+
"""Raises QueryError if the GraphQL response payload contains an errors key."""
366+
if isinstance(resp_dict, dict) and "errors" in resp_dict:
367+
errors = resp_dict["errors"]
368+
all_messages = ""
369+
if isinstance(errors, list):
370+
messages = []
371+
for err in errors:
372+
if isinstance(err, dict):
373+
message = err.get("message")
374+
if message:
375+
messages.append(message)
376+
all_messages = " ".join(messages)
377+
if not all_messages:
378+
all_messages = (
379+
f"GraphQL execution failed: {errors}" if errors
380+
else "GraphQL execution failed."
381+
)
382+
raise QueryError(
383+
message=all_messages,
384+
http_response=resp
385+
)
386+
347387
def _make_gql_request(
348388
self,
349389
url: str,
@@ -364,26 +404,7 @@ def _make_gql_request(
364404
except requests.exceptions.RequestException as error:
365405
raise _utils.handle_platform_error_from_requests(error)
366406

367-
if isinstance(resp_dict, dict) and "errors" in resp_dict:
368-
errors = resp_dict["errors"]
369-
all_messages = ""
370-
if isinstance(errors, list):
371-
messages = [
372-
err.get("message") for err in errors
373-
if isinstance(err, dict) and err.get("message")
374-
]
375-
all_messages = " ".join(messages)
376-
if not all_messages:
377-
all_messages = (
378-
f"GraphQL execution failed: {errors}" if errors
379-
else "GraphQL execution failed."
380-
)
381-
raise exceptions.FirebaseError(
382-
code="query-error",
383-
message=all_messages,
384-
http_response=resp
385-
)
386-
407+
_DataConnectApiClient._check_graphql_errors(resp_dict, resp)
387408
return resp_dict
388409

389410
@staticmethod
@@ -489,8 +510,7 @@ def _parse_graphql_response(
489510
) -> ExecuteGraphqlResponse[_Data]:
490511
"""Parses a raw GraphQL response payload into ExecuteGraphqlResponse."""
491512
if not isinstance(resp_dict, dict):
492-
raise exceptions.FirebaseError(
493-
code="internal-error",
513+
raise exceptions.InternalError(
494514
message="Response payload is not a valid JSON dictionary."
495515
)
496516

tests/test_data_connect.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,10 +1048,10 @@ class AppConfig:
10481048
assert res.data.settings == {1: "first", 2: "second"}
10491049

10501050
def test_parse_graphql_response_non_dict_error(self):
1051-
with pytest.raises(exceptions.FirebaseError) as excinfo:
1051+
with pytest.raises(exceptions.InternalError) as excinfo:
10521052
self.api_client._parse_graphql_response("not-a-dict", dict)
10531053

1054-
assert excinfo.value.code == "internal-error"
1054+
assert excinfo.value.code == exceptions.INTERNAL
10551055
assert str(excinfo.value) == "Response payload is not a valid JSON dictionary."
10561056

10571057
def test_parse_graphql_response_union_types(self):

0 commit comments

Comments
 (0)