Skip to content

Commit 0801c9c

Browse files
author
Pijush Chakraborty
committed
Updating ServerTemplate to accomodate to_json() method
1 parent 065424a commit 0801c9c

File tree

2 files changed

+88
-38
lines changed

2 files changed

+88
-38
lines changed

firebase_admin/remote_config.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"""
1818

1919
import asyncio
20+
import json
2021
import logging
2122
from typing import Dict, Optional, Literal, Union, Any
2223
from enum import Enum
@@ -63,13 +64,12 @@ class CustomSignalOperator(Enum):
6364
SEMANTIC_VERSION_GREATER_EQUAL = "SEMANTIC_VERSION_GREATER_EQUAL"
6465
UNKNOWN = "UNKNOWN"
6566

66-
class ServerTemplateData:
67+
class _ServerTemplateData:
6768
"""Parses, validates and encapsulates template data and metadata."""
68-
def __init__(self, etag, template_data):
69+
def __init__(self, template_data):
6970
"""Initializes a new ServerTemplateData instance.
7071
7172
Args:
72-
etag: The string to be used for initialize the ETag property.
7373
template_data: The data to be parsed for getting the parameters and conditions.
7474
7575
Raises:
@@ -96,8 +96,10 @@ def __init__(self, etag, template_data):
9696
self._version = template_data['version']
9797

9898
self._etag = ''
99-
if etag is not None and isinstance(etag, str):
100-
self._etag = etag
99+
if 'etag' in template_data and isinstance(template_data['etag'], str):
100+
self._etag = template_data['etag']
101+
102+
self._template_data_json = json.dumps(template_data)
101103

102104
@property
103105
def parameters(self):
@@ -115,6 +117,10 @@ def version(self):
115117
def conditions(self):
116118
return self._conditions
117119

120+
@property
121+
def template_data_json(self):
122+
return self._template_data_json
123+
118124

119125
class ServerTemplate:
120126
"""Represents a Server Template with implementations for loading and evaluting the template."""
@@ -170,13 +176,21 @@ def evaluate(self, context: Optional[Dict[str, Union[str, int]]] = None) -> 'Ser
170176
config_values)
171177
return ServerConfig(config_values=self._evaluator.evaluate())
172178

173-
def set(self, template: ServerTemplateData):
179+
def set(self, template_data_json: str):
174180
"""Updates the cache to store the given template is of type ServerTemplateData.
175181
176182
Args:
177-
template: An object of type ServerTemplateData to be cached.
183+
template_data_json: A json string representing ServerTemplateData to be cached.
178184
"""
179-
self._cache = template
185+
template_data_map = json.loads(template_data_json)
186+
self._cache = _ServerTemplateData(template_data_map)
187+
188+
def to_json(self):
189+
"""Provides the server template in a JSON format to be used for initialization later."""
190+
if not self._cache:
191+
raise ValueError("""No Remote Config Server template in cache.
192+
Call load() before calling toJSON().""")
193+
return self._cache.template_data_json
180194

181195

182196
class ServerConfig:
@@ -196,6 +210,9 @@ def get_int(self, key):
196210
def get_float(self, key):
197211
return self._get_value(key).as_float()
198212

213+
def get_value_source(self, key):
214+
return self._get_value(key).get_source()
215+
199216
def _get_value(self, key):
200217
return self._config_values.get(key, _Value('static'))
201218

@@ -233,7 +250,8 @@ async def get_server_template(self):
233250
except requests.exceptions.RequestException as error:
234251
raise self._handle_remote_config_error(error)
235252
else:
236-
return ServerTemplateData(headers.get('etag'), template_data)
253+
template_data['etag'] = headers.get('etag')
254+
return _ServerTemplateData(template_data)
237255

238256
def _get_url(self):
239257
"""Returns project prefix for url, in the format of /v1/projects/${projectId}"""
@@ -633,22 +651,22 @@ async def get_server_template(app: App = None, default_config: Optional[Dict[str
633651
return template
634652

635653
def init_server_template(app: App = None, default_config: Optional[Dict[str, str]] = None,
636-
template_data: Optional[ServerTemplateData] = None):
654+
template_data_json: Optional[str] = None):
637655
"""Initializes a new ServerTemplate instance.
638656
639657
Args:
640658
app: App instance to be used. This is optional and the default app instance will
641659
be used if not present.
642660
default_config: The default config to be used in the evaluated config.
643-
template_data: An optional template data to be set on initialization.
661+
template_data_json: An optional template data JSON to be set on initialization.
644662
645663
Returns:
646664
ServerTemplate: A new ServerTemplate instance initialized with an optional
647665
template and config.
648666
"""
649667
template = ServerTemplate(app=app, default_config=default_config)
650-
if template_data is not None:
651-
template.set(template_data)
668+
if template_data_json is not None:
669+
template.set(template_data_json)
652670
return template
653671

654672
class _Value:

tests/test_remote_config.py

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
CustomSignalOperator,
2222
PercentConditionOperator,
2323
_REMOTE_CONFIG_ATTRIBUTE,
24-
_RemoteConfigService,
25-
ServerTemplateData)
24+
_RemoteConfigService)
2625
from firebase_admin import remote_config, _utils
2726
from tests import testutils
2827

@@ -121,12 +120,12 @@ def test_evaluate_or_and_true_condition_true(self):
121120
},
122121
'parameterGroups': '',
123122
'version': '',
124-
'etag': '123'
123+
'etag': 'etag'
125124
}
126125
server_template = remote_config.init_server_template(
127126
app=app,
128127
default_config=default_config,
129-
template_data=ServerTemplateData('etag', template_data)
128+
template_data_json=json.dumps(template_data)
130129
)
131130

132131
server_config = server_template.evaluate()
@@ -165,12 +164,12 @@ def test_evaluate_or_and_false_condition_false(self):
165164
},
166165
'parameterGroups': '',
167166
'version': '',
168-
'etag': '123'
167+
'etag': 'etag'
169168
}
170169
server_template = remote_config.init_server_template(
171170
app=app,
172171
default_config=default_config,
173-
template_data=ServerTemplateData('etag', template_data)
172+
template_data_json=json.dumps(template_data)
174173
)
175174

176175
server_config = server_template.evaluate()
@@ -196,12 +195,12 @@ def test_evaluate_non_or_condition(self):
196195
},
197196
'parameterGroups': '',
198197
'version': '',
199-
'etag': '123'
198+
'etag': 'etag'
200199
}
201200
server_template = remote_config.init_server_template(
202201
app=app,
203202
default_config=default_config,
204-
template_data=ServerTemplateData('etag', template_data)
203+
template_data_json=json.dumps(template_data)
205204
)
206205

207206
server_config = server_template.evaluate()
@@ -262,12 +261,12 @@ def test_evaluate_return_conditional_values_honor_order(self):
262261
},
263262
'parameterGroups':'',
264263
'version':'',
265-
'etag': '123'
264+
'etag': 'etag'
266265
}
267266
server_template = remote_config.init_server_template(
268267
app=app,
269268
default_config=default_config,
270-
template_data=ServerTemplateData('etag', template_data)
269+
template_data_json=json.dumps(template_data)
271270
)
272271
server_config = server_template.evaluate()
273272
assert server_config.get_string('dog_type') == 'corgi'
@@ -280,7 +279,7 @@ def test_evaluate_default_when_no_param(self):
280279
server_template = remote_config.init_server_template(
281280
app=app,
282281
default_config=default_config,
283-
template_data=ServerTemplateData('etag', template_data)
282+
template_data_json=json.dumps(template_data)
284283
)
285284
server_config = server_template.evaluate()
286285
assert server_config.get_boolean('promo_enabled') == default_config.get('promo_enabled')
@@ -296,7 +295,7 @@ def test_evaluate_default_when_no_default_value(self):
296295
server_template = remote_config.init_server_template(
297296
app=app,
298297
default_config=default_config,
299-
template_data=ServerTemplateData('etag', template_data)
298+
template_data_json=json.dumps(template_data)
300299
)
301300
server_config = server_template.evaluate()
302301
assert server_config.get_string('default_value') == default_config.get('default_value')
@@ -313,7 +312,7 @@ def test_evaluate_default_when_in_default(self):
313312
server_template = remote_config.init_server_template(
314313
app=app,
315314
default_config=default_config,
316-
template_data=ServerTemplateData('etag', template_data)
315+
template_data_json=json.dumps(template_data)
317316
)
318317
server_config = server_template.evaluate()
319318
assert server_config.get_string('inapp_default') == default_config.get('inapp_default')
@@ -328,7 +327,7 @@ def test_evaluate_default_when_defined(self):
328327
server_template = remote_config.init_server_template(
329328
app=app,
330329
default_config=default_config,
331-
template_data=ServerTemplateData('etag', template_data)
330+
template_data_json=json.dumps(template_data)
332331
)
333332
server_config = server_template.evaluate()
334333
assert server_config.get_string('dog_type') == 'shiba'
@@ -342,7 +341,7 @@ def test_evaluate_return_numeric_value(self):
342341
server_template = remote_config.init_server_template(
343342
app=app,
344343
default_config=default_config,
345-
template_data=ServerTemplateData('etag', template_data)
344+
template_data_json=json.dumps(template_data)
346345
)
347346
server_config = server_template.evaluate()
348347
assert server_config.get_int('dog_age') == int(default_config.get('dog_age'))
@@ -356,7 +355,7 @@ def test_evaluate_return_boolean_value(self):
356355
server_template = remote_config.init_server_template(
357356
app=app,
358357
default_config=default_config,
359-
template_data=ServerTemplateData('etag', template_data)
358+
template_data_json=json.dumps(template_data)
360359
)
361360
server_config = server_template.evaluate()
362361
assert server_config.get_boolean('dog_is_cute')
@@ -398,7 +397,7 @@ def test_evaluate_unknown_operator_to_false(self):
398397
server_template = remote_config.init_server_template(
399398
app=app,
400399
default_config=default_config,
401-
template_data=ServerTemplateData('etag', template_data)
400+
template_data_json=json.dumps(template_data)
402401
)
403402
server_config = server_template.evaluate(context)
404403
assert not server_config.get_boolean('is_enabled')
@@ -442,7 +441,7 @@ def test_evaluate_less_or_equal_to_max_to_true(self):
442441
server_template = remote_config.init_server_template(
443442
app=app,
444443
default_config=default_config,
445-
template_data=ServerTemplateData('etag', template_data)
444+
template_data_json=json.dumps(template_data)
446445
)
447446
server_config = server_template.evaluate(context)
448447
assert server_config.get_boolean('is_enabled')
@@ -485,7 +484,7 @@ def test_evaluate_undefined_micropercent_to_false(self):
485484
server_template = remote_config.init_server_template(
486485
app=app,
487486
default_config=default_config,
488-
template_data=ServerTemplateData('etag', template_data)
487+
template_data_json=json.dumps(template_data)
489488
)
490489
server_config = server_template.evaluate(context)
491490
assert not server_config.get_boolean('is_enabled')
@@ -528,7 +527,7 @@ def test_evaluate_undefined_micropercentrange_to_false(self):
528527
server_template = remote_config.init_server_template(
529528
app=app,
530529
default_config=default_config,
531-
template_data=ServerTemplateData('etag', template_data)
530+
template_data_json=json.dumps(template_data)
532531
)
533532
server_config = server_template.evaluate(context)
534533
assert not server_config.get_boolean('is_enabled')
@@ -575,7 +574,7 @@ def test_evaluate_between_min_max_to_true(self):
575574
server_template = remote_config.init_server_template(
576575
app=app,
577576
default_config=default_config,
578-
template_data=ServerTemplateData('etag', template_data)
577+
template_data_json=json.dumps(template_data)
579578
)
580579
server_config = server_template.evaluate(context)
581580
assert server_config.get_boolean('is_enabled')
@@ -622,7 +621,7 @@ def test_evaluate_between_equal_bounds_to_false(self):
622621
server_template = remote_config.init_server_template(
623622
app=app,
624623
default_config=default_config,
625-
template_data=ServerTemplateData('etag', template_data)
624+
template_data_json=json.dumps(template_data)
626625
)
627626
server_config = server_template.evaluate(context)
628627
assert not server_config.get_boolean('is_enabled')
@@ -750,7 +749,7 @@ def evaluate_random_assignments(self, condition, num_of_assignments, mock_app, d
750749
server_template = remote_config.init_server_template(
751750
app=mock_app,
752751
default_config=default_config,
753-
template_data=ServerTemplateData('etag', template_data)
752+
template_data_json=json.dumps(template_data)
754753
)
755754

756755
for _ in range(num_of_assignments):
@@ -814,7 +813,7 @@ def test_evaluate_custom_signal_semantic_version(self,
814813
server_template = remote_config.init_server_template(
815814
app=app,
816815
default_config=default_config,
817-
template_data=ServerTemplateData('etag', template_data)
816+
template_data_json=json.dumps(template_data)
818817
)
819818
server_config = server_template.evaluate(context)
820819
assert server_config.get_boolean('is_enabled') == parameter_value
@@ -917,7 +916,7 @@ def test_init_server_template(self):
917916
template = remote_config.init_server_template(
918917
app=app,
919918
default_config={'default_test': 'default_value'},
920-
template_data=ServerTemplateData('etag', template_data)
919+
template_data_json=json.dumps(template_data)
921920
)
922921

923922
config = template.evaluate()
@@ -949,3 +948,36 @@ async def test_get_server_template(self):
949948

950949
config = template.evaluate()
951950
assert config.get_string('test_key') == 'test_value'
951+
952+
@pytest.mark.asyncio
953+
async def test_server_template_to_json(self):
954+
app = firebase_admin.get_app()
955+
rc_instance = _utils.get_app_service(app,
956+
_REMOTE_CONFIG_ATTRIBUTE, _RemoteConfigService)
957+
958+
recorder = []
959+
response = json.dumps({
960+
'parameters': {
961+
'test_key': {
962+
'defaultValue': {'value': 'test_value'},
963+
'conditionalValues': {}
964+
}
965+
},
966+
'conditions': [],
967+
'version': 'test'
968+
})
969+
970+
expected_template_json = '{"parameters": {' \
971+
'"test_key": {' \
972+
'"defaultValue": {' \
973+
'"value": "test_value"}, ' \
974+
'"conditionalValues": {}}}, "conditions": [], ' \
975+
'"version": "test", "etag": "etag"}'
976+
977+
rc_instance._client.session.mount(
978+
'https://firebaseremoteconfig.googleapis.com',
979+
MockAdapter(response, 200, recorder))
980+
template = await remote_config.get_server_template(app=app)
981+
982+
template_json = template.to_json()
983+
assert template_json == expected_template_json

0 commit comments

Comments
 (0)