def test_some_user_attrs_private():
    uf = UserFilter(config_with_some_attrs_private)
    j = uf.filter_user_props(user)
    assert j == user_with_some_attrs_hidden
def test_leave_anonymous_attr_as_is():
    uf = UserFilter(config_with_all_attrs_private)
    j = uf.filter_user_props(anon_user)
    assert j == anon_user_with_all_attrs_hidden
def test_all_user_attrs_serialized():
    uf = UserFilter(base_config)
    j = uf.filter_user_props(user)
    assert j == user
def test_per_user_private_attr_plus_global_private_attrs():
    uf = UserFilter(config_with_some_attrs_private)
    j = uf.filter_user_props(user_specifying_own_private_attr)
    assert j == user_with_all_attrs_hidden
def test_unknown_top_level_attrs_stripped():
    uf = UserFilter(base_config)
    j = uf.filter_user_props(user_with_unknown_top_level_attrs)
    assert j == user
Example #6
0
 def __init__(self, config):
     self._inline_users = config.inline_users_in_events
     self._user_filter = UserFilter(config)
def test_per_user_private_attr():
    uf = UserFilter(base_config)
    j = uf.filter_user_props(user_specifying_own_private_attr)
    assert j == user_with_own_specified_attr_hidden
Example #8
0
  def __init__(self,sdk_key=None, user=None, use_report=False, evaluation_reasons=False,
                 base_uri='https://app.launchdarkly.com',
                 events_uri='https://mobile.launchdarkly.com',
                 connect_timeout=10,
                 read_timeout=15,
                 events_max_pending=10000,
                 flush_interval=5,
                 stream_uri='https://clientstream.launchdarkly.com',
                 stream=True,
                 verify_ssl=True,
                 defaults=None,
                 send_events=None,
                 events_enabled=True,
                 update_processor_class=None,
                 poll_interval=30,
                 use_ldd=False,
                 feature_store=None,
                 feature_requester_class=None,
                 event_processor_class=None,
                 private_attribute_names=(),
                 all_attributes_private=False,
                 offline=False,
                 user_keys_capacity=1000,
                 user_keys_flush_interval=300,
                 inline_users_in_events=False,
                 http_proxy=None):

    self.__sdk_key = sdk_key

    if defaults is None:
      defaults = {}

    self.__base_uri = base_uri.rstrip('\\')
    self.__events_uri = events_uri.rstrip('\\')
    self.__stream_uri = stream_uri.rstrip('\\')
    self.__update_processor_class = update_processor_class
    self.__stream = stream
    self.__poll_interval = max(poll_interval, 30)
    self.__use_ldd = use_ldd
    self.__feature_store = InMemoryFeatureStore() if not feature_store else feature_store
    self.__event_processor_class = DefaultEventProcessor if not event_processor_class else event_processor_class
    self.__feature_requester_class = feature_requester_class
    self.__connect_timeout = connect_timeout
    self.__read_timeout = read_timeout
    self.__events_max_pending = events_max_pending
    self.__flush_interval = flush_interval
    self.__verify_ssl = verify_ssl
    self.__defaults = defaults
    if offline is True:
        send_events = False
    self.__send_events = events_enabled if send_events is None else send_events
    self.__private_attribute_names = private_attribute_names
    self.__all_attributes_private = all_attributes_private
    self.__offline = offline
    self.__user_keys_capacity = user_keys_capacity
    self.__user_keys_flush_interval = user_keys_flush_interval
    self.__inline_users_in_events = inline_users_in_events
    self.__http_proxy = http_proxy

    self.__use_report = use_report
    self.__evaluation_reasons = evaluation_reasons
    self.__user = user
    self.__user_filter = UserFilter(self)
    if user is not None:
      self.__user_filtered = self.__user_filter.filter_user_props(user)
      self.__user_json = json.dumps(self.__user_filtered).encode('utf-8')
      self.__user_b64 = urlsafe_b64encode(self.__user_json).decode('utf-8')
Example #9
0
class EventOutputFormatter(object):
    def __init__(self, config):
        self._inline_users = config.inline_users_in_events
        self._user_filter = UserFilter(config)

    def make_output_events(self, events, summary):
        events_out = [ self.make_output_event(e) for e in events ]
        if len(summary.counters) > 0:
            events_out.append(self.make_summary_event(summary))
        return events_out
    
    def make_output_event(self, e):
        kind = e['kind']
        if kind == 'feature':
            is_debug = e.get('debug')
            out = {
                'kind': 'debug' if is_debug else 'feature',
                'creationDate': e['creationDate'],
                'key': e['key'],
                'version': e.get('version'),
                'variation': e.get('variation'),
                'value': e.get('value'),
                'default': e.get('default'),
                'prereqOf': e.get('prereqOf')
            }
            if self._inline_users or is_debug:
                out['user'] = self._process_user(e)
            else:
                out['userKey'] = self._get_userkey(e)
            if e.get('reason'):
                out['reason'] = e.get('reason')
            return out
        elif kind == 'identify':
            return {
                'kind': 'identify',
                'creationDate': e['creationDate'],
                'key': self._get_userkey(e),
                'user': self._process_user(e)
            }
        elif kind == 'custom':
            out = {
                'kind': 'custom',
                'creationDate': e['creationDate'],
                'key': e['key'],
                'data': e.get('data')
            }
            if self._inline_users:
                out['user'] = self._process_user(e)
            else:
                out['userKey'] = self._get_userkey(e)
            return out
        elif kind == 'index':
            return {
                'kind': 'index',
                'creationDate': e['creationDate'],
                'user': self._process_user(e)
            }
        else:
            return e

    """
    Transform summarizer data into the format used for the event payload.
    """
    def make_summary_event(self, summary):
        flags_out = dict()
        for ckey, cval in summary.counters.items():
            flag_key, variation, version = ckey
            flag_data = flags_out.get(flag_key)
            if flag_data is None:
                flag_data = { 'default': cval['default'], 'counters': [] }
                flags_out[flag_key] = flag_data
            counter = {
                'count': cval['count'],
                'value': cval['value']
            }
            if variation is not None:
                counter['variation'] = variation
            if version is None:
                counter['unknown'] = True
            else:
                counter['version'] = version
            flag_data['counters'].append(counter)
        return {
            'kind': 'summary',
            'startDate': summary.start_date,
            'endDate': summary.end_date,
            'features': flags_out
        }
    
    def _process_user(self, event):
        filtered = self._user_filter.filter_user_props(event['user'])
        return stringify_attrs(filtered, __USER_ATTRS_TO_STRINGIFY_FOR_EVENTS__)
    
    def _get_userkey(self, event):
        return str(event['user'].get('key'))
def test_per_user_private_attr():
    uf = UserFilter(base_config)
    j = uf.filter_user_props(user_specifying_own_private_attr)
    assert j == user_with_own_specified_attr_hidden
Example #11
0
class MobileConfig(Config):
  def __init__(self,sdk_key=None, user=None, use_report=False, evaluation_reasons=False,
                 base_uri='https://app.launchdarkly.com',
                 events_uri='https://mobile.launchdarkly.com',
                 connect_timeout=10,
                 read_timeout=15,
                 events_max_pending=10000,
                 flush_interval=5,
                 stream_uri='https://clientstream.launchdarkly.com',
                 stream=True,
                 verify_ssl=True,
                 defaults=None,
                 send_events=None,
                 events_enabled=True,
                 update_processor_class=None,
                 poll_interval=30,
                 use_ldd=False,
                 feature_store=None,
                 feature_requester_class=None,
                 event_processor_class=None,
                 private_attribute_names=(),
                 all_attributes_private=False,
                 offline=False,
                 user_keys_capacity=1000,
                 user_keys_flush_interval=300,
                 inline_users_in_events=False,
                 http_proxy=None):

    self.__sdk_key = sdk_key

    if defaults is None:
      defaults = {}

    self.__base_uri = base_uri.rstrip('\\')
    self.__events_uri = events_uri.rstrip('\\')
    self.__stream_uri = stream_uri.rstrip('\\')
    self.__update_processor_class = update_processor_class
    self.__stream = stream
    self.__poll_interval = max(poll_interval, 30)
    self.__use_ldd = use_ldd
    self.__feature_store = InMemoryFeatureStore() if not feature_store else feature_store
    self.__event_processor_class = DefaultEventProcessor if not event_processor_class else event_processor_class
    self.__feature_requester_class = feature_requester_class
    self.__connect_timeout = connect_timeout
    self.__read_timeout = read_timeout
    self.__events_max_pending = events_max_pending
    self.__flush_interval = flush_interval
    self.__verify_ssl = verify_ssl
    self.__defaults = defaults
    if offline is True:
        send_events = False
    self.__send_events = events_enabled if send_events is None else send_events
    self.__private_attribute_names = private_attribute_names
    self.__all_attributes_private = all_attributes_private
    self.__offline = offline
    self.__user_keys_capacity = user_keys_capacity
    self.__user_keys_flush_interval = user_keys_flush_interval
    self.__inline_users_in_events = inline_users_in_events
    self.__http_proxy = http_proxy

    self.__use_report = use_report
    self.__evaluation_reasons = evaluation_reasons
    self.__user = user
    self.__user_filter = UserFilter(self)
    if user is not None:
      self.__user_filtered = self.__user_filter.filter_user_props(user)
      self.__user_json = json.dumps(self.__user_filtered).encode('utf-8')
      self.__user_b64 = urlsafe_b64encode(self.__user_json).decode('utf-8')


  def copy_with_new_sdk_key(self, new_sdk_key):
    """Returns a new ``Config`` instance that is the same as this one, except for having a different SDK key.
    :param string new_sdk_key: the new SDK key
    :rtype: ldclient.config.Config
    """
    return MobileConfig(sdk_key=new_sdk_key,
                  base_uri=self.__base_uri,
                  events_uri=self.__events_uri,
                  connect_timeout=self.__connect_timeout,
                  read_timeout=self.__read_timeout,
                  events_max_pending=self.__events_max_pending,
                  flush_interval=self.__flush_interval,
                  stream_uri=self.__stream_uri,
                  stream=self.__stream,
                  verify_ssl=self.__verify_ssl,
                  defaults=self.__defaults,
                  send_events=self.__send_events,
                  update_processor_class=self.__update_processor_class,
                  poll_interval=self.__poll_interval,
                  use_ldd=self.__use_ldd,
                  feature_store=self.__feature_store,
                  feature_requester_class=self.__feature_requester_class,
                  event_processor_class=self.__event_processor_class,
                  private_attribute_names=self.__private_attribute_names,
                  all_attributes_private=self.__all_attributes_private,
                  offline=self.__offline,
                  user_keys_capacity=self.__user_keys_capacity,
                  user_keys_flush_interval=self.__user_keys_flush_interval,
                  inline_users_in_events=self.__inline_users_in_events,
                  use_report=self.__use_report,
                  evaluation_reasons=self.__evaluation_reasons,
                  user=self.__user)
  def copy_with_new_user(self, new_user, new_sdk_key=None):
    """Returns a new ``Config`` instance that is the same as this one, except for having a different SDK key.
    :param string new_sdk_key: the new SDK key
    :rtype: ldclient.config.Config
    """
    key = new_sdk_key or self.sdk_key
    
    return MobileConfig(sdk_key=key,
                  base_uri=self.__base_uri,
                  events_uri=self.__events_uri,
                  connect_timeout=self.__connect_timeout,
                  read_timeout=self.__read_timeout,
                  events_max_pending=self.__events_max_pending,
                  flush_interval=self.__flush_interval,
                  stream_uri=self.__stream_uri,
                  stream=self.__stream,
                  verify_ssl=self.__verify_ssl,
                  defaults=self.__defaults,
                  send_events=self.__send_events,
                  update_processor_class=self.__update_processor_class,
                  poll_interval=self.__poll_interval,
                  use_ldd=self.__use_ldd,
                  feature_store=self.__feature_store,
                  feature_requester_class=self.__feature_requester_class,
                  event_processor_class=self.__event_processor_class,
                  private_attribute_names=self.__private_attribute_names,
                  all_attributes_private=self.__all_attributes_private,
                  offline=self.__offline,
                  user_keys_capacity=self.__user_keys_capacity,
                  user_keys_flush_interval=self.__user_keys_flush_interval,
                  inline_users_in_events=self.__inline_users_in_events,
                  use_report=self.__use_report,
                  evaluation_reasons=self.__evaluation_reasons,
                  user=new_user)


  @property
  def evaluation_reasons(self):
    return self.__evaluation_reasons
  @property
  def use_report(self):
    return self.__use_report
  @property
  def user(self):
    return self.__user
  @property
  def user_b64(self):
    return self.__user_b64
  @property
  def user_json(self):
    return self.__user_json


  # for internal use only - probably should be part of the client logic
  def get_default(self, key, default):
    return default if key not in self.__defaults else self.__defaults[key]

  @property
  def sdk_key(self):
      return self.__sdk_key

  @property
  def base_uri(self):
      return self.__base_uri

  # for internal use only - also no longer used, will remove
  @property
  def get_latest_flags_uri(self):
      return self.__base_uri + '/msdk/evalx'

  # for internal use only - should construct the URL path in the events code, not here
  @property
  def events_uri(self):
      return self.__events_uri + '/mobile/events/bulk'

  # for internal use only
  @property
  def stream_base_uri(self):
      return self.__stream_uri

  # for internal use only - should construct the URL path in the streaming code, not here
  @property
  def stream_uri(self):
      return self.__stream_uri + '/msdk/evalx'

  @property
  def update_processor_class(self):
      return self.__update_processor_class

  @property
  def stream(self):
      return self.__stream

  @property
  def poll_interval(self):
      return self.__poll_interval

  @property
  def use_ldd(self):
      return self.__use_ldd

  @property
  def feature_store(self):
      return self.__feature_store

  @property
  def event_processor_class(self):
      return self.__event_processor_class

  @property
  def feature_requester_class(self):
      return self.__feature_requester_class

  @property
  def connect_timeout(self):
      return self.__connect_timeout

  @property
  def read_timeout(self):
      return self.__read_timeout

  @property
  def events_enabled(self):
      return self.__send_events

  @property
  def send_events(self):
      return self.__send_events

  @property
  def events_max_pending(self):
      return self.__events_max_pending

  @property
  def flush_interval(self):
      return self.__flush_interval

  @property
  def verify_ssl(self):
      return self.__verify_ssl

  @property
  def private_attribute_names(self):
      return list(self.__private_attribute_names)

  @property
  def all_attributes_private(self):
      return self.__all_attributes_private

  @property
  def offline(self):
      return self.__offline

  @property
  def user_keys_capacity(self):
      return self.__user_keys_capacity

  @property
  def user_keys_flush_interval(self):
      return self.__user_keys_flush_interval

  @property
  def inline_users_in_events(self):
      return self.__inline_users_in_events

  @property
  def http_proxy(self):
      return self.__http_proxy
  


  def _validate(self):
      if self.offline is False and self.sdk_key is None or self.sdk_key == '':
          log.warning("Missing or blank sdk_key.")
def test_some_user_attrs_private():
    uf = UserFilter(config_with_some_attrs_private)
    j = uf.filter_user_props(user)
    assert j == user_with_some_attrs_hidden
def test_all_user_attrs_serialized():
    uf = UserFilter(base_config)
    j = uf.filter_user_props(user)
    assert j == user
def test_leave_anonymous_attr_as_is():
    uf = UserFilter(config_with_all_attrs_private)
    j = uf.filter_user_props(anon_user)
    assert j == anon_user_with_all_attrs_hidden
def test_unknown_top_level_attrs_stripped():
    uf = UserFilter(base_config)
    j = uf.filter_user_props(user_with_unknown_top_level_attrs)
    assert j == user
def test_per_user_private_attr_plus_global_private_attrs():
    uf = UserFilter(config_with_some_attrs_private)
    j = uf.filter_user_props(user_specifying_own_private_attr)
    assert j == user_with_all_attrs_hidden