@@ -200,51 +200,99 @@ def track_event(
200200 )
201201 return {event_name : False }
202202
203- def set_attribute (self , attribute_key : str , attribute_value : Any , context : Dict ):
203+ def set_attribute (
204+ self , key_or_map : Any , value_or_context : Any , context : Dict = None
205+ ):
204206 """
205207 Sets an attribute for a user in the context provided.
206- This method validates the types of the inputs before proceeding with the API call.
208+ This method can be called in two ways:
209+ 1. set_attribute(key, value, context) - Sets a single attribute
210+ 2. set_attribute(attribute_map, context) - Sets multiple attributes
207211
208- :param attribute_key: The key of the attribute to set.
209- :param attribute_value: The value of the attribute to set.
210- :param context: The context in which the attribute is being set.
212+ The attribute values must be either strings, integers, or booleans.
213+
214+ :param key_or_map: Either the attribute key (str) or a map of attributes (Dict)
215+ :param value_or_context: Either the attribute value or the context (if key_or_map is a Dict)
216+ :param context: The context in which the attribute is being set (only used when setting single attribute)
211217 """
212218 api_name = "set_attribute"
213219
214220 try :
215-
216221 LogManager .get_instance ().debug (
217222 debug_messages .get ("API_CALLED" ).format (apiName = api_name )
218223 )
219224
220- # Validate featureKey is a string
221- if not is_string (attribute_key ):
222- LogManager .get_instance ().error (
223- error_messages .get ("API_INVALID_PARAM" ).format (
224- apiName = api_name ,
225- key = "attribute_key" ,
226- type = type (attribute_key ).__name__ ,
227- correctType = "string" ,
225+ # Determine which calling pattern is being used
226+ if context is not None :
227+ # Single attribute pattern: (key, value, context)
228+ if not is_string (key_or_map ):
229+ LogManager .get_instance ().error (
230+ error_messages .get ("API_INVALID_PARAM" ).format (
231+ apiName = api_name ,
232+ key = "key" ,
233+ type = type (key_or_map ).__name__ ,
234+ correctType = "string" ,
235+ )
236+ )
237+ raise TypeError ("TypeError: key should be a string" )
238+
239+ if not isinstance (value_or_context , (str , int , bool , float )):
240+ LogManager .get_instance ().error (
241+ error_messages .get ("API_INVALID_PARAM" ).format (
242+ apiName = api_name ,
243+ key = "value" ,
244+ type = type (value_or_context ).__name__ ,
245+ correctType = "string, integer, float, or boolean" ,
246+ )
247+ )
248+ raise TypeError (
249+ "TypeError: value should be a string, integer, float, or boolean"
228250 )
229- )
230- raise TypeError ("TypeError: attribute_key should be a string" )
231251
232- if (
233- not is_string (attribute_value )
234- and not isinstance (attribute_value , int )
235- and not isinstance (attribute_value , bool )
236- ):
237- LogManager .get_instance ().error (
238- error_messages .get ("API_INVALID_PARAM" ).format (
239- apiName = api_name ,
240- key = "attribute_value" ,
241- type = type (attribute_value ).__name__ ,
242- correctType = "string or int or bool" ,
252+ attribute_map = {key_or_map : value_or_context }
253+ user_context = context
254+ else :
255+ # Multiple attributes pattern: (attribute_map, context)
256+ if not is_object (key_or_map ) or not key_or_map :
257+ LogManager .get_instance ().error (
258+ error_messages .get ("API_INVALID_PARAM" ).format (
259+ apiName = api_name ,
260+ key = "attribute_map" ,
261+ type = type (key_or_map ).__name__ ,
262+ correctType = "object" ,
263+ )
264+ )
265+ raise TypeError (
266+ "TypeError: attribute_map should be a non-empty object"
243267 )
244- )
245- raise TypeError (
246- "TypeError: attribute_value should be an string or int or bool"
247- )
268+
269+ # Validate all keys and values in the attribute map
270+ for key , value in key_or_map .items ():
271+ if not is_string (key ):
272+ LogManager .get_instance ().error (
273+ error_messages .get ("API_INVALID_PARAM" ).format (
274+ apiName = api_name ,
275+ key = "key" ,
276+ type = type (key ).__name__ ,
277+ correctType = "string" ,
278+ )
279+ )
280+ raise TypeError ("TypeError: key should be a string" )
281+ if not isinstance (value , (str , int , bool , float )):
282+ LogManager .get_instance ().error (
283+ error_messages .get ("API_INVALID_PARAM" ).format (
284+ apiName = api_name ,
285+ key = f"value for key '{ key } '" ,
286+ type = type (value ).__name__ ,
287+ correctType = "string, integer, float, or boolean" ,
288+ )
289+ )
290+ raise TypeError (
291+ f"TypeError: value for key '{ key } ' should be a string, integer, float or boolean"
292+ )
293+
294+ attribute_map = key_or_map
295+ user_context = value_or_context
248296
249297 # Validate settings are loaded and valid
250298 if not SettingsManager .is_settings_valid (self .original_settings ):
@@ -253,19 +301,17 @@ def set_attribute(self, attribute_key: str, attribute_value: Any, context: Dict)
253301 )
254302 raise ValueError ("Invalid Settings" )
255303
256- # Validate user ID is present in context
257- if not context or "id" not in context :
304+ if not user_context or "id" not in user_context :
258305 LogManager .get_instance ().error (
259306 error_messages .get ("API_CONTEXT_INVALID" )
260307 )
261308 raise ValueError ("Invalid context" )
262309
263- context_model = ContextModel (context )
310+ context_model = ContextModel (user_context )
264311
265- # Fetch the feature flag value using FlagApi
266312 set_attribute_api = SetAttributeApi ()
267313 set_attribute_api .set_attribute (
268- self ._settings , attribute_key , attribute_value , context_model
314+ self ._settings , attribute_map , context_model
269315 )
270316 return
271317
0 commit comments