1717""" 
1818
1919import  asyncio 
20+ import  json 
2021import  logging 
22+ import  threading 
2123from  typing  import  Dict , Optional , Literal , Union , Any 
2224from  enum  import  Enum 
2325import  re 
@@ -63,13 +65,12 @@ class CustomSignalOperator(Enum):
6365    SEMANTIC_VERSION_GREATER_EQUAL  =  "SEMANTIC_VERSION_GREATER_EQUAL" 
6466    UNKNOWN  =  "UNKNOWN" 
6567
66- class  ServerTemplateData :
68+ class  _ServerTemplateData :
6769    """Parses, validates and encapsulates template data and metadata.""" 
68-     def  __init__ (self , etag ,  template_data ):
70+     def  __init__ (self , template_data ):
6971        """Initializes a new ServerTemplateData instance. 
7072
7173        Args: 
72-             etag: The string to be used for initialize the ETag property. 
7374            template_data: The data to be parsed for getting the parameters and conditions. 
7475
7576        Raises: 
@@ -96,8 +97,10 @@ def __init__(self, etag, template_data):
9697            self ._version  =  template_data ['version' ]
9798
9899        self ._etag  =  '' 
99-         if  etag  is  not None  and  isinstance (etag , str ):
100-             self ._etag  =  etag 
100+         if  'etag'  in  template_data  and  isinstance (template_data ['etag' ], str ):
101+             self ._etag  =  template_data ['etag' ]
102+ 
103+         self ._template_data_json  =  json .dumps (template_data )
101104
102105    @property  
103106    def  parameters (self ):
@@ -115,6 +118,10 @@ def version(self):
115118    def  conditions (self ):
116119        return  self ._conditions 
117120
121+     @property  
122+     def  template_data_json (self ):
123+         return  self ._template_data_json 
124+ 
118125
119126class  ServerTemplate :
120127    """Represents a Server Template with implementations for loading and evaluting the template.""" 
@@ -132,6 +139,7 @@ def __init__(self, app: App = None, default_config: Optional[Dict[str, str]] = N
132139        # fetched from RC servers via the load API, or via the set API. 
133140        self ._cache  =  None 
134141        self ._stringified_default_config : Dict [str , str ] =  {}
142+         self ._lock  =  threading .RLock ()
135143
136144        # RC stores all remote values as string, but it's more intuitive 
137145        # to declare default values with specific types, so this converts 
@@ -142,7 +150,9 @@ def __init__(self, app: App = None, default_config: Optional[Dict[str, str]] = N
142150
143151    async  def  load (self ):
144152        """Fetches the server template and caches the data.""" 
145-         self ._cache  =  await  self ._rc_service .get_server_template ()
153+         rc_server_template  =  await  self ._rc_service .get_server_template ()
154+         with  self ._lock :
155+             self ._cache  =  rc_server_template 
146156
147157    def  evaluate (self , context : Optional [Dict [str , Union [str , int ]]] =  None ) ->  'ServerConfig' :
148158        """Evaluates the cached server template to produce a ServerConfig. 
@@ -161,22 +171,40 @@ def evaluate(self, context: Optional[Dict[str, Union[str, int]]] = None) -> 'Ser
161171                            Call load() before calling evaluate().""" )
162172        context  =  context  or  {}
163173        config_values  =  {}
174+ 
175+         with  self ._lock :
176+             template_conditions  =  self ._cache .conditions 
177+             template_parameters  =  self ._cache .parameters 
178+ 
164179        # Initializes config Value objects with default values. 
165180        if  self ._stringified_default_config  is  not None :
166181            for  key , value  in  self ._stringified_default_config .items ():
167182                config_values [key ] =  _Value ('default' , value )
168-         self ._evaluator  =  _ConditionEvaluator (self . _cache . conditions ,
169-                                               self . _cache . parameters , context ,
183+         self ._evaluator  =  _ConditionEvaluator (template_conditions ,
184+                                               template_parameters , context ,
170185                                              config_values )
171186        return  ServerConfig (config_values = self ._evaluator .evaluate ())
172187
173-     def  set (self , template :  ServerTemplateData ):
188+     def  set (self , template_data_json :  str ):
174189        """Updates the cache to store the given template is of type ServerTemplateData. 
175190
176191        Args: 
177-           template: An object of type  ServerTemplateData to be cached. 
192+           template_data_json: A json string representing  ServerTemplateData to be cached. 
178193        """ 
179-         self ._cache  =  template 
194+         template_data_map  =  json .loads (template_data_json )
195+         template_data  =  _ServerTemplateData (template_data_map )
196+ 
197+         with  self ._lock :
198+             self ._cache  =  template_data 
199+ 
200+     def  to_json (self ):
201+         """Provides the server template in a JSON format to be used for initialization later.""" 
202+         if  not  self ._cache :
203+             raise  ValueError ("""No Remote Config Server template in cache. 
204+                             Call load() before calling toJSON().""" )
205+         with  self ._lock :
206+             template_json  =  self ._cache .template_data_json 
207+         return  template_json 
180208
181209
182210class  ServerConfig :
@@ -185,17 +213,25 @@ def __init__(self, config_values):
185213        self ._config_values  =  config_values  # dictionary of param key to values 
186214
187215    def  get_boolean (self , key ):
216+         """Returns the value as a boolean.""" 
188217        return  self ._get_value (key ).as_boolean ()
189218
190219    def  get_string (self , key ):
220+         """Returns the value as a string.""" 
191221        return  self ._get_value (key ).as_string ()
192222
193223    def  get_int (self , key ):
224+         """Returns the value as an integer.""" 
194225        return  self ._get_value (key ).as_int ()
195226
196227    def  get_float (self , key ):
228+         """Returns the value as a float.""" 
197229        return  self ._get_value (key ).as_float ()
198230
231+     def  get_value_source (self , key ):
232+         """Returns the source of the value.""" 
233+         return  self ._get_value (key ).get_source ()
234+ 
199235    def  _get_value (self , key ):
200236        return  self ._config_values .get (key , _Value ('static' ))
201237
@@ -233,7 +269,8 @@ async def get_server_template(self):
233269        except  requests .exceptions .RequestException  as  error :
234270            raise  self ._handle_remote_config_error (error )
235271        else :
236-             return  ServerTemplateData (headers .get ('etag' ), template_data )
272+             template_data ['etag' ] =  headers .get ('etag' )
273+             return  _ServerTemplateData (template_data )
237274
238275    def  _get_url (self ):
239276        """Returns project prefix for url, in the format of /v1/projects/${projectId}""" 
@@ -633,22 +670,22 @@ async def get_server_template(app: App = None, default_config: Optional[Dict[str
633670    return  template 
634671
635672def  init_server_template (app : App  =  None , default_config : Optional [Dict [str , str ]] =  None ,
636-                          template_data : Optional [ServerTemplateData ] =  None ):
673+                          template_data_json : Optional [str ] =  None ):
637674    """Initializes a new ServerTemplate instance. 
638675
639676    Args: 
640677        app: App instance to be used. This is optional and the default app instance will 
641678            be used if not present. 
642679        default_config: The default config to be used in the evaluated config. 
643-         template_data : An optional template data to be set on initialization. 
680+         template_data_json : An optional template data JSON  to be set on initialization. 
644681
645682    Returns: 
646683        ServerTemplate: A new ServerTemplate instance initialized with an optional 
647684        template and config. 
648685    """ 
649686    template  =  ServerTemplate (app = app , default_config = default_config )
650-     if  template_data  is  not None :
651-         template .set (template_data )
687+     if  template_data_json  is  not None :
688+         template .set (template_data_json )
652689    return  template 
653690
654691class  _Value :
0 commit comments