Skip to content

Commit c84ce6f

Browse files
authored
feat(dataconnect): Implemented Data Connect service client and comprehensive test suite (#955)
* feat(fdc): Added unit tests for Data Connect client factory and _DataConnectService * Testing ConnectorConfig, client function, and service * refactor(dataconnect): Addressed code review feedback and standardized docstrings/synatx Refactored test suite to use module-level BASE_CONFIG and added parameterized client caching tests. * feat(dataconnect): Implemented foundational Data Connect client and service architecture Implemented ConnectorConfig dataclass, DataConnect client instance, _DataConnectService caching layer, and public client() factory function. Fixed mock recursion in test_client_successful using lambda delegation in test_data_connect.py. * chore(dataconnect): Standardized test suite formatting to achieve 10.00/10 linter score * refactor(dataconnect): Addressed code review feedback and optimized test suite structure Removed duplicate client factory caching tests, moved app service loader test to unit test class, added connector property validation, and formatted parameter lists. * chore(fdc): Updated documentation and moved integration tests Added missing copyright headers and improved docstrings with proper formatting in dataconnect.py. Moved TestDataConnectServiceIntegration from tests/test_data_connect.py into a dedicated integration test file under integration/test_data_connect.py. * refactor(fdc): Returned TestDataConnectServiceIntegration to tests/test_data_connect.py Moved TestDataConnectServiceIntegration back to tests/test_data_connect.py and removed integration/test_data_connect.py. * test(fdc): Renamed integration test class to TestDataConnectServiceWorkflow Renamed TestDataConnectServiceIntegration to TestDataConnectServiceWorkflow in tests/test_data_connect.py to clarify that it is an in-memory unit/functional test rather than a network integration test.
1 parent c5e66f8 commit c84ce6f

2 files changed

Lines changed: 457 additions & 0 deletions

File tree

firebase_admin/dataconnect.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright 2026 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Firebase Data Connect module.
16+
17+
This module contains utilities for accessing Firebase Data Connect services associated with
18+
Firebase apps.
19+
"""
20+
21+
from dataclasses import dataclass
22+
from typing import Dict, Optional
23+
24+
from firebase_admin import _utils, App
25+
26+
__all__ = ['ConnectorConfig', 'DataConnect', 'client']
27+
28+
_DATA_CONNECT_ATTRIBUTE = '_data_connect'
29+
30+
@dataclass(frozen=True)
31+
class ConnectorConfig:
32+
"""A configuration object for DataConnect.
33+
34+
Attributes:
35+
service_id: A string representing the Google Cloud project ID of the service.
36+
location: A string representing the region of the service.
37+
connector: A string representing the name of the connector.
38+
"""
39+
40+
service_id: str
41+
location: str
42+
connector: str
43+
44+
def __post_init__(self):
45+
if not isinstance(self.service_id, str):
46+
raise ValueError("service_id must be a string")
47+
if not self.service_id:
48+
raise ValueError("service_id cannot be empty")
49+
if not isinstance(self.location, str):
50+
raise ValueError("location must be a string")
51+
if not self.location:
52+
raise ValueError("location cannot be empty")
53+
if not isinstance(self.connector, str):
54+
raise ValueError("connector must be a string")
55+
if not self.connector:
56+
raise ValueError("connector cannot be empty")
57+
58+
59+
class DataConnect:
60+
"""Represents a Firebase Data Connect client instance.
61+
62+
This client provides access to the Firebase Data Connect service
63+
for a specific Firebase app and connector configuration.
64+
65+
Attributes:
66+
app: The Firebase App instance for this client.
67+
config: The ConnectorConfig object specifying the service ID, location, and connector name.
68+
"""
69+
70+
def __init__(self, app: App, config: ConnectorConfig) -> None:
71+
"""Initializes a DataConnect client instance. """
72+
self._app: App = app
73+
self._config = config
74+
75+
@property
76+
def app(self) -> App:
77+
return self._app
78+
79+
@property
80+
def config(self) -> ConnectorConfig:
81+
return self._config
82+
83+
84+
class _DataConnectService:
85+
"""Service that maintains a collection of DataConnect clients."""
86+
87+
def __init__(self, app: App) -> None:
88+
self._app: App = app
89+
self._clients: Dict[ConnectorConfig, DataConnect] = {}
90+
91+
def get_client(self, config: ConnectorConfig) -> DataConnect:
92+
"""Creates a client based on the ConnectorConfig. These clients are cached."""
93+
if not isinstance(config, ConnectorConfig):
94+
raise ValueError("Config must be of type firebase_admin.dataconnect.ConnectorConfig")
95+
if config not in self._clients:
96+
self._clients[config] = DataConnect(app=self._app, config=config)
97+
return self._clients[config]
98+
99+
100+
def client(config: ConnectorConfig, app: Optional[App] = None) -> DataConnect:
101+
"""Returns a DataConnect client for the specified configuration.
102+
103+
This function does not make any RPC calls.
104+
105+
Args:
106+
config: A ConnectorConfig instance specifying the service ID, location,
107+
and connector name.
108+
app: An App instance (optional). Defaults to the default Firebase App.
109+
110+
Returns:
111+
DataConnect: A handle to the specified DataConnect client instance.
112+
113+
Raises:
114+
ValueError: If config argument is not an instance of ConnectorConfig, or if
115+
app is an invalid instance of App.
116+
"""
117+
118+
if not isinstance(config, ConnectorConfig):
119+
raise ValueError("Config must be of type firebase_admin.dataconnect.ConnectorConfig")
120+
121+
# must check whether app has a _DataConnectService attached to it yet
122+
dc_service = _utils.get_app_service(app, _DATA_CONNECT_ATTRIBUTE, _DataConnectService)
123+
124+
return dc_service.get_client(config)

0 commit comments

Comments
 (0)