Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ffe47d4

Browse files
author
Pijush Chakraborty
committed
Adding comments and unit tests
1 parent 7dc118e commit ffe47d4

File tree

3 files changed

+140
-80
lines changed

3 files changed

+140
-80
lines changed

firebase_admin/remote_config.py

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

19+
import json
1920
from typing import Dict, Optional
2021
from firebase_admin import App, _http_client, _utils
2122
import firebase_admin
2223

2324
_REMOTE_CONFIG_ATTRIBUTE = '_remoteconfig'
2425

2526
class ServerTemplateData:
26-
"""Represents a Server Template Data class.
27-
"""
28-
def __init__(self, resp):
29-
30-
self._parameters = resp.body.parameters
31-
self._conditions = resp.body.conditions
32-
self._version = resp.body.version
33-
self._parameterGroups = resp.body.parameterGroups
34-
self._etag = resp.headers.get('etag')
27+
"""Represents a Server Template Data class."""
28+
def __init__(self, headers, response_json):
29+
self._parameters = response_json['parameters']
30+
self._conditions = response_json['conditions']
31+
self._version = response_json['version']
32+
self._parameter_groups = response_json['parameterGroups']
33+
self._etag = headers.get('ETag')
3534

3635
@property
3736
def parameters(self):
38-
# TODO: convert to Parameters
3937
return self._parameters
4038

4139
@property
@@ -49,79 +47,55 @@ def version(self):
4947
@property
5048
def conditions(self):
5149
return self._conditions
52-
53-
@property
54-
def conditions(self):
55-
return self._parameterGroups
56-
57-
class Parameter:
58-
""" Representation of a remote config parameter."""
59-
60-
def __init__(self, default_value):
61-
self._default_value = default_value # ParameterValue
62-
63-
@property
64-
def default_value(self):
65-
return self._default_value
66-
67-
68-
class ParameterValue:
69-
""" Base class to represent remote parameter values. A
70-
ParameterValue could be either an ExplicitParameterValue or an
71-
InAppDefaultValue. """
72-
73-
74-
class ExplicitParameterValue(ParameterValue):
75-
def __init__(self, value):
76-
self._value = value
7750

7851
@property
79-
def value(self):
80-
return self._value
81-
82-
class InAppDefaultValue(ParameterValue):
83-
def __init__(self, use_in_app_default):
84-
self._use_in_app_default = use_in_app_default
85-
86-
@property
87-
def use_in_app_default(self):
88-
return self._use_in_app_default
52+
def parameter_groups(self):
53+
return self._parameter_groups
8954

9055

9156
class ServerTemplate:
92-
"""Represents a Server Template with implementations for loading and evaluting the tempalte.
93-
"""
57+
"""Represents a Server Template with implementations for loading and evaluting the tempalte."""
9458
def __init__(self, app: App = None, default_config: Optional[Dict[str, str]] = None):
95-
self._rc_service = _utils.get_app_service(app, _REMOTE_CONFIG_ATTRIBUTE, _RemoteConfigService)
59+
"""Initializes a ServerTemplate instance.
9660
97-
# Field to represent the cached template. This gets set when the template is
61+
Args:
62+
app: App instance to be used. This is optional and the default app instance will
63+
be used if not present.
64+
default_config: The default config to be used in the evaluated config.
65+
"""
66+
self._rc_service = _utils.get_app_service(app,
67+
_REMOTE_CONFIG_ATTRIBUTE, _RemoteConfigService)
68+
69+
# This gets set when the template is
9870
# fetched from RC servers via the load API, or via the set API.
9971
self._cache = None
10072
if default_config is not None:
101-
for key in default_config:
102-
self._stringified_default_config[key] = default_config[key]
73+
self._stringified_default_config = json.dumps(default_config)
10374
else:
104-
self._stringified_default_config[key] = None
75+
self._stringified_default_config = None
10576

10677
async def load(self):
78+
"""Fetches the server template and caches the data."""
10779
self._cache = await self._rc_service.getServerTemplate()
10880

109-
def evaluate(self, context: Optional[Dict[str, str | int]]):
110-
# Logic to process the cached template into a ServerConfig here
111-
# TODO: add Condition evaluator
112-
self._evaluator = ConditionEvaluator(self._cache.conditions, context)
81+
def evaluate(self, context):
82+
# Logic to process the cached template into a ServerConfig here.
83+
# TODO: Add Condition evaluator.
84+
self._evaluator = _ConditionEvaluator(self._cache.conditions, context)
11385
return ServerConfig(config_values=self._evaluator.evaluate())
11486

11587
def set(self, template):
116-
if isinstance(template, str):
117-
self._cache = ServerTemplateData(template)
118-
elif isinstance(template, ServerTemplateData):
88+
"""Updates the cache to store the given template is of type ServerTemplateData.
89+
90+
Args:
91+
template: An object of type ServerTemplateData to be cached.
92+
"""
93+
if isinstance(template, ServerTemplateData):
11994
self._cache = template
12095

12196

12297
class ServerConfig:
123-
"""Represents a Remote Config Server Side Config.
124-
"""
98+
"""Represents a Remote Config Server Side Config."""
12599
def __init__(self, config_values):
126100
self._config_values = config_values # dictionary of param key to values
127101

@@ -137,13 +111,18 @@ def get_int(self, key):
137111
def get_value(self, key):
138112
return self._config_values[key]
139113

140-
class _RemoteConfigService:
141-
""" Internal class that facilitates sending requests to the Firebase Remote
142-
Config backend API. """
143114

115+
class _RemoteConfigService:
116+
"""Internal class that facilitates sending requests to the Firebase Remote
117+
Config backend API.
118+
"""
144119
def __init__(self, app):
145-
# Initialize a JsonHttpClient with basic inputs. Referenced other
146-
# products' code in the Python SDK for what basic inputs to use.
120+
"""Initialize a JsonHttpClient with necessary inputs.
121+
122+
Args:
123+
app: App instance to be used for fetching app specific details required
124+
for initializing the http client.
125+
"""
147126
remote_config_base_url = 'https://firebaseremoteconfig.googleapis.com'
148127
self._project_id = app.project_id
149128
app_credential = app.credential.get_credential()
@@ -157,24 +136,23 @@ def __init__(self, app):
157136

158137

159138
def get_server_template(self):
160-
# Requests for server template and converts the response to
161-
# ServerTemplateData
139+
"""Requests for a server template and converts the response to an instance of
140+
ServerTemplateData for storing the template parameters and conditions."""
162141
url_prefix = self._get_url_prefix()
163-
response_json = self._client.body('get',
164-
url=url_prefix+'/namespaces/ \
165-
firebase-server/serverRemoteConfig')
166-
return ServerTemplateData(response_json)
142+
headers, response_json = self._client.headers_and_body('get',
143+
url=url_prefix+'/namespaces/ \
144+
firebase-server/serverRemoteConfig')
145+
return ServerTemplateData(headers, response_json)
167146

168147
def _get_url_prefix(self):
169148
# Returns project prefix for url, in the format of
170149
# /v1/projects/${projectId}
171150
return "/v1/projects/{0}".format(self._project_id)
172-
173151

174-
class _ConditionEvaluator:
175-
""" Internal class that facilitates sending requests to the Firebase Remote
176-
Config backend API. """
177152

153+
class _ConditionEvaluator:
154+
"""Internal class that facilitates sending requests to the Firebase Remote
155+
Config backend API."""
178156
def __init__(self, context, conditions):
179157
self._context = context
180158
self._conditions = conditions
@@ -185,13 +163,35 @@ def evaluate(self):
185163

186164

187165
async def get_server_template(app: App = None, default_config: Optional[Dict[str, str]] = None):
188-
template = init_server_template(app, default_config)
166+
"""Initializes a new ServerTemplate instance and fetches the server template.
167+
168+
Args:
169+
app: App instance to be used. This is optional and the default app instance will
170+
be used if not present.
171+
default_config: The default config to be used in the evaluated config.
172+
173+
Returns:
174+
ServerTemplate: An object having the cached server template to be used for evaluation.
175+
"""
176+
template = init_server_template(app=app, default_config=default_config)
189177
await template.load()
190178
return template
191179

192-
def init_server_template(app: App = None, default_config: Optional[Dict[str, str]] = None,
180+
def init_server_template(app: App = None, default_config: Optional[Dict[str, str]] = None,
193181
template_data: Optional[ServerTemplateData] = None):
194-
template = ServerTemplate(app, default_config=default_config)
182+
"""Initializes a new ServerTemplate instance.
183+
184+
Args:
185+
app: App instance to be used. This is optional and the default app instance will
186+
be used if not present.
187+
default_config: The default config to be used in the evaluated config.
188+
template_data: An optional template data to be set on initialization.
189+
190+
Returns:
191+
ServerTemplate: A new ServerTemplate instance initialized with an optional
192+
template and config.
193+
"""
194+
template = ServerTemplate(app=app, default_config=default_config)
195195
if template_data is not None:
196196
template.set(template_data)
197197
return template

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=15)
212+
assert delta <= timedelta(seconds=30)
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: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright 2017 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+
"""Tests for firebase_admin.remote_config."""
16+
import json
17+
import firebase_admin
18+
from firebase_admin.remote_config import _REMOTE_CONFIG_ATTRIBUTE, _RemoteConfigService
19+
20+
from firebase_admin import _utils
21+
from tests import testutils
22+
23+
class MockAdapter(testutils.MockAdapter):
24+
"""A Mock HTTP Adapter that Firebase Remote Config with ETag in header."""
25+
26+
ETAG = '0'
27+
28+
def __init__(self, data, status, recorder, etag=ETAG):
29+
testutils.MockAdapter.__init__(self, data, status, recorder)
30+
self._etag = etag
31+
32+
def send(self, request, **kwargs):
33+
resp = super(MockAdapter, self).send(request, **kwargs)
34+
resp.headers = {'ETag': self._etag}
35+
return resp
36+
37+
38+
class TestGetServerTemplate:
39+
_DEFAULT_APP = firebase_admin.initialize_app(testutils.MockCredential(), name='no_project_id')
40+
_RC_INSTANCE = _utils.get_app_service(_DEFAULT_APP,
41+
_REMOTE_CONFIG_ATTRIBUTE, _RemoteConfigService)
42+
_DEFAULT_RESPONSE = json.dumps({
43+
'parameters': {
44+
'test_key': 'test_value'
45+
},
46+
'conditions': {},
47+
'parameterGroups': {},
48+
'version': 'test'
49+
})
50+
51+
def test_rc_instance_get_server_template(self):
52+
recorder = []
53+
self._RC_INSTANCE._client.session.mount(
54+
'https://firebaseremoteconfig.googleapis.com',
55+
MockAdapter(self._DEFAULT_RESPONSE, 200, recorder))
56+
57+
template = self._RC_INSTANCE.get_server_template()
58+
59+
assert template.parameters == dict(test_key="test_value")
60+
assert str(template.version) == 'test'

0 commit comments

Comments
 (0)