Skip to content

Commit cde8b09

Browse files
author
Pijush Chakraborty
committed
Addressing PR comments and fixing async flow during fetch call
1 parent 681d353 commit cde8b09

File tree

3 files changed

+32
-20
lines changed

3 files changed

+32
-20
lines changed

firebase_admin/remote_config.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
This module has required APIs for the clients to use Firebase Remote Config with python.
1717
"""
1818

19+
import asyncio
1920
import json
2021
from typing import Any, Dict, Optional
2122
import requests
@@ -25,7 +26,7 @@
2526
_REMOTE_CONFIG_ATTRIBUTE = '_remoteconfig'
2627

2728
class ServerTemplateData:
28-
"""Represents a Server Template Data class."""
29+
"""Parses, validates and encapsulates template data and metadata."""
2930
def __init__(self, etag, template_data):
3031
"""Initializes a new ServerTemplateData instance.
3132
@@ -78,7 +79,7 @@ def conditions(self):
7879

7980

8081
class ServerTemplate:
81-
"""Represents a Server Template with implementations for loading and evaluting the tempalte."""
82+
"""Represents a Server Template with implementations for loading and evaluting the template."""
8283
def __init__(self, app: App = None, default_config: Optional[Dict[str, str]] = None):
8384
"""Initializes a ServerTemplate instance.
8485
@@ -93,14 +94,18 @@ def __init__(self, app: App = None, default_config: Optional[Dict[str, str]] = N
9394
# This gets set when the template is
9495
# fetched from RC servers via the load API, or via the set API.
9596
self._cache = None
97+
self._stringified_default_config: Dict[str,str] = {}
98+
99+
# RC stores all remote values as string, but it's more intuitive
100+
# to declare default values with specific types, so this converts
101+
# the external declaration to an internal string representation.
96102
if default_config is not None:
97-
self._stringified_default_config = json.dumps(default_config)
98-
else:
99-
self._stringified_default_config = None
103+
for key in default_config:
104+
self._stringified_default_config[key] = str(default_config[key])
100105

101106
async def load(self):
102107
"""Fetches the server template and caches the data."""
103-
self._cache = self._rc_service.get_server_template()
108+
self._cache = await self._rc_service.get_server_template()
104109

105110
def evaluate(self):
106111
# Logic to process the cached template into a ServerConfig here.
@@ -157,21 +162,23 @@ def __init__(self, app):
157162
base_url=remote_config_base_url,
158163
headers=rc_headers, timeout=timeout)
159164

160-
def get_server_template(self):
165+
async def get_server_template(self):
161166
"""Requests for a server template and converts the response to an instance of
162167
ServerTemplateData for storing the template parameters and conditions."""
163-
url_prefix = self._get_url_prefix()
164168
try:
165-
headers, response_json = self._client.headers_and_body(
166-
'get', url=url_prefix+'/namespaces/firebase-server/serverRemoteConfig')
169+
loop = asyncio.get_event_loop()
170+
headers, template_data = await loop.run_in_executor(None,
171+
self._client.headers_and_body,
172+
'get', self._get_url())
167173
except requests.exceptions.RequestException as error:
168174
raise self._handle_remote_config_error(error)
169175
else:
170-
return ServerTemplateData(headers.get('etag'), response_json)
176+
return ServerTemplateData(headers.get('etag'), template_data)
171177

172-
def _get_url_prefix(self):
178+
def _get_url(self):
173179
"""Returns project prefix for url, in the format of /v1/projects/${projectId}"""
174-
return "/v1/projects/{0}".format(self._project_id)
180+
return "/v1/projects/{0}/namespaces/firebase-server/serverRemoteConfig".format(
181+
self._project_id)
175182

176183
@classmethod
177184
def _handle_remote_config_error(cls, error: Any):

tests/test_functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def test_task_options(self, task_opts_params):
209209

210210
schedule_time = datetime.fromisoformat(task['schedule_time'][:-1])
211211
delta = abs(schedule_time - _SCHEDULE_TIME)
212-
assert delta <= timedelta(seconds=30)
212+
assert delta <= timedelta(seconds=15)
213213

214214
assert task['dispatch_deadline'] == '200s'
215215
assert task['http_request']['headers']['x-test-header'] == 'test-header-value'

tests/test_remote_config.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def send(self, request, **kwargs):
3838
return resp
3939

4040

41-
class TestRemoteConfigServiceClient:
41+
class TestRemoteConfigService:
42+
"""Tests methods on _RemoteConfigService"""
4243
@classmethod
4344
def setup_class(cls):
4445
cred = testutils.MockCredential()
@@ -48,7 +49,8 @@ def setup_class(cls):
4849
def teardown_class(cls):
4950
testutils.cleanup_apps()
5051

51-
def test_rc_instance_get_server_template(self):
52+
@pytest.mark.asyncio
53+
async def test_rc_instance_get_server_template(self):
5254
recorder = []
5355
response = json.dumps({
5456
'parameters': {
@@ -64,13 +66,14 @@ def test_rc_instance_get_server_template(self):
6466
'https://firebaseremoteconfig.googleapis.com',
6567
MockAdapter(response, 200, recorder))
6668

67-
template = rc_instance.get_server_template()
69+
template = await rc_instance.get_server_template()
6870

6971
assert template.parameters == dict(test_key="test_value")
7072
assert str(template.version) == 'test'
7173
assert str(template.etag) == 'etag'
7274

73-
def test_rc_instance_get_server_template_empty_params(self):
75+
@pytest.mark.asyncio
76+
async def test_rc_instance_get_server_template_empty_params(self):
7477
recorder = []
7578
response = json.dumps({
7679
'conditions': [],
@@ -83,14 +86,15 @@ def test_rc_instance_get_server_template_empty_params(self):
8386
'https://firebaseremoteconfig.googleapis.com',
8487
MockAdapter(response, 200, recorder))
8588

86-
template = rc_instance.get_server_template()
89+
template = await rc_instance.get_server_template()
8790

8891
assert template.parameters == {}
8992
assert str(template.version) == 'test'
9093
assert str(template.etag) == 'etag'
9194

9295

93-
class TestRemoteConfigService:
96+
class TestRemoteConfigModule:
97+
"""Tests methods on firebase_admin.remote_config"""
9498
@classmethod
9599
def setup_class(cls):
96100
cred = testutils.MockCredential()
@@ -112,6 +116,7 @@ def test_init_server_template(self):
112116

113117
template = remote_config.init_server_template(
114118
app=app,
119+
default_config={'default_test': 'default_value'},
115120
template_data=ServerTemplateData('etag', template_data)
116121
)
117122

0 commit comments

Comments
 (0)