Beispiel #1
0
    def test_put_fetch_contains_ip_address_disabled(self):
        """Test storing and retrieving splits in redis."""
        adapter = _build_default_client({})
        cfg = DEFAULT_CONFIG.copy()
        cfg.update({'IPAddressesEnabled': False})
        metadata = get_metadata(cfg)
        storage = RedisTelemetryStorage(adapter, metadata)
        try:

            storage.inc_counter('counter1')
            storage.inc_counter('counter1')
            storage.inc_counter('counter2')
            assert adapter.get(storage._get_counter_key('counter1')) == '2'
            assert adapter.get(storage._get_counter_key('counter2')) == '1'

            storage.inc_latency('latency1', 3)
            storage.inc_latency('latency1', 3)
            storage.inc_latency('latency2', 6)
            assert adapter.get(storage._get_latency_key('latency1', 3)) == '2'
            assert adapter.get(storage._get_latency_key('latency2', 6)) == '1'

            storage.put_gauge('gauge1', 3)
            storage.put_gauge('gauge2', 1)
            assert adapter.get(storage._get_gauge_key('gauge1')) == '3'
            assert adapter.get(storage._get_gauge_key('gauge2')) == '1'

        finally:
            adapter.delete(storage._get_counter_key('counter1'),
                           storage._get_counter_key('counter2'),
                           storage._get_latency_key('latency1', 3),
                           storage._get_latency_key('latency2', 6),
                           storage._get_gauge_key('gauge1'),
                           storage._get_gauge_key('gauge2'))
Beispiel #2
0
    def test_post_impressions_ip_address_disabled(self, mocker):
        """Test impressions posting API call."""
        httpclient = mocker.Mock(spec=client.HttpClient)
        httpclient.post.return_value = client.HttpResponse(200, '')
        cfg = DEFAULT_CONFIG.copy()
        cfg.update({'IPAddressesEnabled': False})
        sdk_metadata = get_metadata(cfg)
        impressions_api = impressions.ImpressionsAPI(httpclient,
                                                     'some_api_key',
                                                     sdk_metadata)
        response = impressions_api.flush_impressions(self.impressions)

        call_made = httpclient.post.mock_calls[0]

        # validate positional arguments
        assert call_made[1] == ('events', '/testImpressions/bulk',
                                'some_api_key')

        # validate key-value args (headers)
        assert call_made[2]['extra_headers'] == {
            'SplitSDKVersion': 'python-%s' % __version__,
        }

        # validate key-value args (body)
        assert call_made[2]['body'] == self.expectedImpressions
def _get_config(user_config):
    """
    Get sdk configuration using defaults + user overrides.

    :param user_config: User configuration.
    :type user_config: dict

    :return: Calculated configuration.
    :rtype: dict
    """
    sdk_config = DEFAULT_CONFIG.copy()
    sdk_config.update(user_config)
    return sdk_config
Beispiel #4
0
    def test_put_fetch_contains_ip_address_disabled(self):
        """Test storing and retrieving splits in redis."""
        adapter = _build_default_client({})
        try:
            cfg = DEFAULT_CONFIG.copy()
            cfg.update({'IPAddressesEnabled': False})
            self._put_events(adapter, get_metadata(cfg))

            evts = adapter.lrange('SPLITIO.events', 0, 2)
            assert len(evts) == 3
            for rawEvent in evts:
                event = json.loads(rawEvent)
                assert event['m']['i'] == 'NA'
                assert event['m']['n'] == 'NA'
        finally:
            adapter.delete('SPLITIO.events')
Beispiel #5
0
def _build_uwsgi_factory(config):
    """Build and return a split factory with redis-based storage."""
    cfg = DEFAULT_CONFIG.copy()
    cfg.update(config)
    sdk_metadata = util.get_metadata(cfg)
    uwsgi_adapter = get_uwsgi()
    storages = {
        'splits': UWSGISplitStorage(uwsgi_adapter),
        'segments': UWSGISegmentStorage(uwsgi_adapter),
        'impressions': UWSGIImpressionStorage(uwsgi_adapter),
        'events': UWSGIEventStorage(uwsgi_adapter),
        'telemetry': UWSGITelemetryStorage(uwsgi_adapter)
    }
    return SplitFactory(storages,
                        cfg['labelsEnabled'],
                        impression_listener=_wrap_impression_listener(
                            cfg['impressionListener'], sdk_metadata))
Beispiel #6
0
def _build_redis_factory(config):
    """Build and return a split factory with redis-based storage."""
    cfg = DEFAULT_CONFIG.copy()
    cfg.update(config)
    sdk_metadata = util.get_metadata(config)
    redis_adapter = redis.build(config)
    storages = {
        'splits': RedisSplitStorage(redis_adapter),
        'segments': RedisSegmentStorage(redis_adapter),
        'impressions': RedisImpressionsStorage(redis_adapter, sdk_metadata),
        'events': RedisEventsStorage(redis_adapter, sdk_metadata),
        'telemetry': RedisTelemetryStorage(redis_adapter, sdk_metadata)
    }
    return SplitFactory(storages,
                        cfg['labelsEnabled'],
                        impression_listener=_wrap_impression_listener(
                            cfg['impressionListener'], sdk_metadata))
Beispiel #7
0
    def setup_method(self):
        """Prepare storages with test data."""
        metadata = SdkMetadata('python-1.2.3', 'some_ip', 'some_name')
        redis_client = build(DEFAULT_CONFIG.copy())
        split_storage = RedisSplitStorage(redis_client, True)
        segment_storage = RedisSegmentStorage(redis_client)

        split_fn = os.path.join(os.path.dirname(__file__), 'files',
                                'splitChanges.json')
        with open(split_fn, 'r') as flo:
            data = json.loads(flo.read())
        for split in data['splits']:
            redis_client.set(split_storage._get_key(split['name']),
                             json.dumps(split))
        redis_client.set(split_storage._SPLIT_TILL_KEY, data['till'])

        segment_fn = os.path.join(os.path.dirname(__file__), 'files',
                                  'segmentEmployeesChanges.json')
        with open(segment_fn, 'r') as flo:
            data = json.loads(flo.read())
        redis_client.sadd(segment_storage._get_key(data['name']),
                          *data['added'])
        redis_client.set(segment_storage._get_till_key(data['name']),
                         data['till'])

        segment_fn = os.path.join(os.path.dirname(__file__), 'files',
                                  'segmentHumanBeignsChanges.json')
        with open(segment_fn, 'r') as flo:
            data = json.loads(flo.read())
        redis_client.sadd(segment_storage._get_key(data['name']),
                          *data['added'])
        redis_client.set(segment_storage._get_till_key(data['name']),
                         data['till'])

        storages = {
            'splits': split_storage,
            'segments': segment_storage,
            'impressions': RedisImpressionsStorage(redis_client, metadata),
            'events': RedisEventsStorage(redis_client, metadata),
        }
        impmanager = ImpressionsManager(storages['impressions'].put,
                                        ImpressionsMode.DEBUG)
        recorder = PipelinedRecorder(redis_client.pipeline, impmanager,
                                     storages['events'],
                                     storages['impressions'])
        self.factory = SplitFactory('some_api_key', storages, True, recorder)  # pylint:disable=attribute-defined-outside-init
Beispiel #8
0
    def test_post_impressions(self, mocker):
        """Test impressions posting API call."""
        httpclient = mocker.Mock(spec=client.HttpClient)
        httpclient.post.return_value = client.HttpResponse(200, '')
        cfg = DEFAULT_CONFIG.copy()
        cfg.update({
            'IPAddressesEnabled': True,
            'machineName': 'some_machine_name',
            'machineIp': '123.123.123.123'
        })
        sdk_metadata = get_metadata(cfg)
        impressions_api = impressions.ImpressionsAPI(httpclient,
                                                     'some_api_key',
                                                     sdk_metadata)
        response = impressions_api.flush_impressions(self.impressions)

        call_made = httpclient.post.mock_calls[0]

        # validate positional arguments
        assert call_made[1] == ('events', '/testImpressions/bulk',
                                'some_api_key')

        # validate key-value args (headers)
        assert call_made[2]['extra_headers'] == {
            'SplitSDKVersion': 'python-%s' % __version__,
            'SplitSDKMachineIP': '123.123.123.123',
            'SplitSDKMachineName': 'some_machine_name',
            'SplitSDKImpressionsMode': 'OPTIMIZED'
        }

        # validate key-value args (body)
        assert call_made[2]['body'] == self.expectedImpressions

        httpclient.reset_mock()

        def raise_exception(*args, **kwargs):
            raise client.HttpClientException('some_message')

        httpclient.post.side_effect = raise_exception
        with pytest.raises(APIException) as exc_info:
            response = impressions_api.flush_impressions(self.impressions)
            assert exc_info.type == APIException
            assert exc_info.value.message == 'some_message'
Beispiel #9
0
    def test_auth(self, mocker):
        """Test auth API call."""
        token = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ.eyJ4LWFibHktY2FwYWJpbGl0eSI6IntcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X3NlZ21lbnRzXCI6W1wic3Vic2NyaWJlXCJdLFwiTnpNMk1ESTVNemMwX01UZ3lOVGcxTVRnd05nPT1fc3BsaXRzXCI6W1wic3Vic2NyaWJlXCJdLFwiY29udHJvbF9wcmlcIjpbXCJzdWJzY3JpYmVcIixcImNoYW5uZWwtbWV0YWRhdGE6cHVibGlzaGVyc1wiXSxcImNvbnRyb2xfc2VjXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl19IiwieC1hYmx5LWNsaWVudElkIjoiY2xpZW50SWQiLCJleHAiOjE2MDIwODgxMjcsImlhdCI6MTYwMjA4NDUyN30.5_MjWonhs6yoFhw44hNJm3H7_YMjXpSW105DwjjppqE"
        httpclient = mocker.Mock(spec=client.HttpClient)
        payload = '{{"pushEnabled": true, "token": "{token}"}}'.format(
            token=token)
        cfg = DEFAULT_CONFIG.copy()
        cfg.update({
            'IPAddressesEnabled': True,
            'machineName': 'some_machine_name',
            'machineIp': '123.123.123.123'
        })
        sdk_metadata = get_metadata(cfg)
        httpclient.get.return_value = client.HttpResponse(200, payload)
        auth_api = auth.AuthAPI(httpclient, 'some_api_key', sdk_metadata)
        response = auth_api.authenticate()

        assert response.push_enabled == True
        assert response.token == token

        call_made = httpclient.get.mock_calls[0]

        # validate positional arguments
        assert call_made[1] == ('auth', '/v2/auth', 'some_api_key')

        # validate key-value args (headers)
        assert call_made[2]['extra_headers'] == {
            'SplitSDKVersion': 'python-%s' % __version__,
            'SplitSDKMachineIP': '123.123.123.123',
            'SplitSDKMachineName': 'some_machine_name'
        }

        httpclient.reset_mock()

        def raise_exception(*args, **kwargs):
            raise client.HttpClientException('some_message')

        httpclient.get.side_effect = raise_exception
        with pytest.raises(APIException) as exc_info:
            response = auth_api.authenticate()
            assert exc_info.type == APIException
            assert exc_info.value.message == 'some_message'
Beispiel #10
0
def _build_localhost_factory(config):
    """Build and return a localhost factory for testing/development purposes."""
    cfg = DEFAULT_CONFIG.copy()
    cfg.update(config)
    storages = {
        'splits': InMemorySplitStorage(),
        'segments': InMemorySegmentStorage(
        ),  # not used, just to avoid possible future errors.
        'impressions': LocalhostImpressionsStorage(),
        'events': LocalhostEventsStorage(),
        'telemetry': LocalhostTelemetryStorage()
    }

    ready_event = threading.Event()
    tasks = {
        'splits':
        LocalhostSplitSynchronizationTask(cfg['splitFile'], storages['splits'],
                                          cfg['featuresRefreshRate'],
                                          ready_event)
    }
    tasks['splits'].start()
    return SplitFactory('localhost', storages, False, None, tasks, ready_event)
Beispiel #11
0
def _build_in_memory_factory(api_key, config, sdk_url=None, events_url=None):  # pylint: disable=too-many-locals
    """Build and return a split factory tailored to the supplied config."""
    if not input_validator.validate_factory_instantiation(api_key):
        return None

    cfg = DEFAULT_CONFIG.copy()
    cfg.update(config)
    http_client = HttpClient(sdk_url=sdk_url,
                             events_url=events_url,
                             timeout=cfg.get('connectionTimeout'))

    sdk_metadata = util.get_metadata(cfg)
    apis = {
        'splits': SplitsAPI(http_client, api_key),
        'segments': SegmentsAPI(http_client, api_key),
        'impressions': ImpressionsAPI(http_client, api_key, sdk_metadata),
        'events': EventsAPI(http_client, api_key, sdk_metadata),
        'telemetry': TelemetryAPI(http_client, api_key, sdk_metadata)
    }

    if not input_validator.validate_apikey_type(apis['segments']):
        return None

    storages = {
        'splits': InMemorySplitStorage(),
        'segments': InMemorySegmentStorage(),
        'impressions': InMemoryImpressionStorage(cfg['impressionsQueueSize']),
        'events': InMemoryEventStorage(cfg['eventsQueueSize']),
        'telemetry': InMemoryTelemetryStorage()
    }

    # Synchronization flags
    splits_ready_flag = threading.Event()
    segments_ready_flag = threading.Event()
    sdk_ready_flag = threading.Event()

    tasks = {
        'splits':
        SplitSynchronizationTask(apis['splits'], storages['splits'],
                                 cfg['featuresRefreshRate'],
                                 splits_ready_flag),
        'segments':
        SegmentSynchronizationTask(apis['segments'], storages['segments'],
                                   storages['splits'],
                                   cfg['segmentsRefreshRate'],
                                   segments_ready_flag),
        'impressions':
        ImpressionsSyncTask(apis['impressions'], storages['impressions'],
                            cfg['impressionsRefreshRate'],
                            cfg['impressionsBulkSize']),
        'events':
        EventsSyncTask(
            apis['events'],
            storages['events'],
            cfg['eventsPushRate'],
            cfg['eventsBulkSize'],
        ),
        'telemetry':
        TelemetrySynchronizationTask(apis['telemetry'], storages['telemetry'],
                                     cfg['metricsRefreshRate'])
    }

    # Start tasks that have no dependencies
    tasks['splits'].start()
    tasks['impressions'].start()
    tasks['events'].start()
    tasks['telemetry'].start()

    storages['events'].set_queue_full_hook(tasks['events'].flush)
    storages['impressions'].set_queue_full_hook(tasks['impressions'].flush)

    def split_ready_task():
        """Wait for splits to be ready and start fetching segments."""
        splits_ready_flag.wait()
        tasks['segments'].start()

    def segment_ready_task():
        """Wait for segments to be ready and set the main ready flag."""
        segments_ready_flag.wait()
        sdk_ready_flag.set()

    split_completion_thread = threading.Thread(target=split_ready_task)
    split_completion_thread.setDaemon(True)
    split_completion_thread.start()
    segment_completion_thread = threading.Thread(target=segment_ready_task)
    segment_completion_thread.setDaemon(True)
    segment_completion_thread.start()
    return SplitFactory(api_key,
                        storages,
                        cfg['labelsEnabled'],
                        apis,
                        tasks,
                        sdk_ready_flag,
                        impression_listener=_wrap_impression_listener(
                            cfg['impressionListener'], sdk_metadata))