Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
41 changes: 40 additions & 1 deletion firebase_admin/dataconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
from dataclasses import dataclass, asdict, is_dataclass
import typing
from typing import Any, Dict, Generic, Optional, Type, TypeVar, Union

Comment thread
mk2023 marked this conversation as resolved.
import requests

import firebase_admin
from firebase_admin import _utils, _http_client, App
from firebase_admin import _utils, _http_client, App, exceptions

__all__ = [
'ConnectorConfig',
Expand Down Expand Up @@ -334,3 +337,39 @@ def _get_headers(self) -> Dict[str, str]:
"X-Firebase-Client": f"fire-admin-python/{firebase_admin.__version__}",
"x-goog-api-client": _utils.get_metrics_header(),
}

def _make_gql_request(
self,
url: str,
headers: Dict[str, str],
payload: Dict[str, Any]
) -> Dict[str, Any]:
"""Make a GraphQL request to the Data Connect service."""
if url is None or headers is None or payload is None:
raise ValueError("url, headers, and payload must all be specified.")

try:
resp_dict, resp = self._http_client.body_and_response(
'post',
url=url,
headers=headers,
json=payload
)
except requests.exceptions.RequestException as error:
raise _utils.handle_platform_error_from_requests(error)

if resp_dict and "errors" in resp_dict:
errors = resp_dict["errors"]
if isinstance(errors, list) and len(errors) > 0:
messages = [
Comment thread
mk2023 marked this conversation as resolved.
Outdated
err.get("message") for err in errors
if isinstance(err, dict) and err.get("message")
]
all_messages = " ".join(messages)
raise exceptions.FirebaseError(
code="query-error",
message=all_messages,
http_response=resp
)
Comment thread
mk2023 marked this conversation as resolved.
Outdated

return resp_dict
60 changes: 58 additions & 2 deletions tests/test_data_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@

from google.auth import credentials as google_auth_credentials
import pytest

import requests

import firebase_admin
from firebase_admin import _utils
from firebase_admin import _utils, _http_client, exceptions
from firebase_admin import dataconnect
from tests import testutils

Expand Down Expand Up @@ -724,3 +724,59 @@ def test_get_headers(self):
assert isinstance(headers, dict)
assert headers.get("X-Firebase-Client") == f"fire-admin-python/{firebase_admin.__version__}"
assert headers.get("x-goog-api-client") == _utils.get_metrics_header()


class TestDataConnectApiClientMakeGqlRequest:

def setup_method(self):
self.cred = testutils.MockCredential()
self.app = firebase_admin.initialize_app(
self.cred, options={'projectId': 'test-project'}
)
self.api_client = dataconnect._DataConnectApiClient(BASE_CONFIG, self.app)

def teardown_method(self, method):
del method
testutils.cleanup_apps()

@mock.patch.object(_http_client.JsonHttpClient, "body_and_response")
def test_make_gql_request_success(self, mock_body_and_response):
mock_response = mock.Mock(spec=requests.Response)
mock_body_and_response.return_value = ({"data": "val"}, mock_response)
url = "https://example.com/endpoint"
headers = {"key": "val"}
payload = {"query": "foo"}

res = self.api_client._make_gql_request(url, headers, payload)
assert res == {"data": "val"}
mock_body_and_response.assert_called_once_with(
"post", url=url, headers=headers, json=payload
)

def test_make_gql_request_missing_url(self):
headers = {"key": "val"}
payload = {"query": "foo"}
with pytest.raises(ValueError, match="url, headers, and payload must all be specified."):
self.api_client._make_gql_request(None, headers, payload)

def test_make_gql_request_missing_headers(self):
url = "https://example.com/endpoint"
payload = {"query": "foo"}
with pytest.raises(ValueError, match="url, headers, and payload must all be specified."):
self.api_client._make_gql_request(url, None, payload)

def test_make_gql_request_missing_payload(self):
url = "https://example.com/endpoint"
headers = {"key": "val"}
with pytest.raises(ValueError, match="url, headers, and payload must all be specified."):
self.api_client._make_gql_request(url, headers, None)

@mock.patch.object(_http_client.JsonHttpClient, "body_and_response")
def test_make_gql_request_error(self, mock_body_and_response):
mock_body_and_response.side_effect = requests.exceptions.RequestException()
url = "https://example.com/endpoint"
headers = {"key": "val"}
payload = {"query": "foo"}

with pytest.raises(exceptions.FirebaseError):
self.api_client._make_gql_request(url, headers, payload)
Comment thread
mk2023 marked this conversation as resolved.