public void Init(User user, FullDataSet data)
        {
            _dataStore.Init(user, data, true);

            ImmutableDictionary <string, FeatureFlag> oldValues, newValues;

            lock (_lastValuesLock)
            {
                _lastValues.TryGetValue(user.Key, out oldValues);
                var builder = ImmutableDictionary.CreateBuilder <string, FeatureFlag>();
                foreach (var newEntry in data.Items)
                {
                    var newFlag = newEntry.Value.Item;
                    if (newFlag != null)
                    {
                        builder.Add(newEntry.Key, newFlag);
                    }
                }
                newValues   = builder.ToImmutable();
                _lastValues = _lastValues.SetItem(user.Key, newValues);
            }

            UpdateStatus(DataSourceState.Valid, null);

            if (oldValues != null)
            {
                List <FlagValueChangeEvent> events = new List <FlagValueChangeEvent>();

                foreach (var newEntry in newValues)
                {
                    var newFlag = newEntry.Value;
                    if (oldValues.TryGetValue(newEntry.Key, out var oldFlag))
                    {
                        if (newFlag.Variation != oldFlag.Variation)
                        {
                            events.Add(new FlagValueChangeEvent(newEntry.Key,
                                                                oldFlag.Value, newFlag.Value, false));
                        }
                    }
                    else
                    {
                        events.Add(new FlagValueChangeEvent(newEntry.Key,
                                                            LdValue.Null, newFlag.Value, false));
                    }
                }
                foreach (var oldEntry in oldValues)
                {
                    if (!newValues.ContainsKey(oldEntry.Key))
                    {
                        events.Add(new FlagValueChangeEvent(oldEntry.Key,
                                                            oldEntry.Value.Value, LdValue.Null, true));
                    }
                }
                foreach (var e in events)
                {
                    _taskExecutor.ScheduleEvent(e, FlagValueChanged);
                }
            }
        }
Example #2
0
        /// <inheritdoc/>
        public async Task <bool> IdentifyAsync(User user)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            User newUser = _userDecorator.DecorateUser(user);
            User oldUser = newUser; // this initialization is overwritten below, it's only here to satisfy the compiler

            LockUtils.WithWriteLock(_stateLock, () =>
            {
                oldUser = _user;
                _user   = newUser;
            });

            // If we had cached data for the new user, set the current in-memory flag data state to use
            // that data, so that any Variation calls made before Identify has completed will use the
            // last known values. If we did not have cached data, then we update the current in-memory
            // state to reflect that there is no flag data, so that Variation calls done before completion
            // will receive default values rather than the previous user's values. This does not modify
            // any flags in persistent storage, and (currently) it does *not* trigger any FlagValueChanged
            // events from FlagTracker.
            var cachedData = _dataStore.GetCachedData(newUser);

            if (cachedData != null)
            {
                _log.Debug("Identify found cached flag data for the new user");
            }
            _dataStore.Init(
                newUser,
                cachedData ?? new DataStoreTypes.FullDataSet(null),
                false // false means "don't rewrite the flags to persistent storage"
                );

            EventProcessorIfEnabled().RecordIdentifyEvent(new EventProcessorTypes.IdentifyEvent
            {
                Timestamp = UnixMillisecondTime.Now,
                User      = user
            });
            if (oldUser.Anonymous && !newUser.Anonymous && !_config.AutoAliasingOptOut)
            {
                EventProcessorIfEnabled().RecordAliasEvent(new EventProcessorTypes.AliasEvent
                {
                    Timestamp    = UnixMillisecondTime.Now,
                    User         = user,
                    PreviousUser = oldUser
                });
            }

            return(await _connectionManager.SetUser(newUser));
        }
Example #3
0
        LdClient(Configuration configuration, User user, TimeSpan startWaitTime)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            _config = configuration ?? throw new ArgumentNullException(nameof(configuration));
            var diagnosticStore = _config.DiagnosticOptOut ? null :
                                  new ClientDiagnosticStore(_config, startWaitTime);
            var diagnosticDisabler = _config.DiagnosticOptOut ? null :
                                     new DiagnosticDisablerImpl();

            _context      = new LdClientContext(configuration, this, diagnosticStore, diagnosticDisabler);
            _log          = _context.BaseLogger;
            _taskExecutor = _context.TaskExecutor;
            diagnosticStore?.SetContext(_context);

            _log.Info("Starting LaunchDarkly Client {0}", Version);

            var persistenceConfiguration = (configuration.PersistenceConfigurationBuilder ?? Components.Persistence())
                                           .CreatePersistenceConfiguration(_context);

            _dataStore = new FlagDataManager(
                configuration.MobileKey,
                persistenceConfiguration,
                _log.SubLogger(LogNames.DataStoreSubLog)
                );

            _userDecorator = new UserDecorator(configuration.DeviceInfo ?? new DefaultDeviceInfo(),
                                               _dataStore.PersistentStore);
            _user = _userDecorator.DecorateUser(user);

            // If we had cached data for the new user, set the current in-memory flag data state to use
            // that data, so that any Variation calls made before Identify has completed will use the
            // last known values.
            var cachedData = _dataStore.GetCachedData(_user);

            if (cachedData != null)
            {
                _log.Debug("Cached flag data is available for this user");
                _dataStore.Init(_user, cachedData.Value, false); // false means "don't rewrite the flags to persistent storage"
            }

            var dataSourceUpdateSink = new DataSourceUpdateSinkImpl(
                _dataStore,
                configuration.Offline,
                _taskExecutor,
                _log.SubLogger(LogNames.DataSourceSubLog)
                );

            _dataSourceUpdateSink = dataSourceUpdateSink;

            _dataSourceStatusProvider = new DataSourceStatusProviderImpl(dataSourceUpdateSink);
            _flagTracker = new FlagTrackerImpl(dataSourceUpdateSink);

            var dataSourceFactory = configuration.DataSourceFactory ?? Components.StreamingDataSource();

            _connectivityStateManager = Factory.CreateConnectivityStateManager(configuration);
            var isConnected = _connectivityStateManager.IsConnected;

            diagnosticDisabler?.SetDisabled(!isConnected || configuration.Offline);

            _eventProcessor = (configuration.EventProcessorFactory ?? Components.SendEvents())
                              .CreateEventProcessor(_context);
            _eventProcessor.SetOffline(configuration.Offline || !isConnected);

            _connectionManager = new ConnectionManager(
                _context,
                dataSourceFactory,
                _dataSourceUpdateSink,
                _eventProcessor,
                diagnosticDisabler,
                configuration.EnableBackgroundUpdating,
                _user,
                _log
                );
            _connectionManager.SetForceOffline(configuration.Offline);
            _connectionManager.SetNetworkEnabled(isConnected);
            if (configuration.Offline)
            {
                _log.Info("Starting LaunchDarkly client in offline mode");
            }

            _connectivityStateManager.ConnectionChanged += networkAvailable =>
            {
                _log.Debug("Setting online to {0} due to a connectivity change event", networkAvailable);
                _ = _connectionManager.SetNetworkEnabled(networkAvailable);  // do not await the result
            };

            // Send an initial identify event, but only if we weren't explicitly set to be offline

            if (!configuration.Offline)
            {
                _eventProcessor.RecordIdentifyEvent(new EventProcessorTypes.IdentifyEvent
                {
                    Timestamp = UnixMillisecondTime.Now,
                    User      = user
                });
            }

            _backgroundModeManager = _config.BackgroundModeManager ?? new DefaultBackgroundModeManager();
            _backgroundModeManager.BackgroundModeChanged += OnBackgroundModeChanged;
        }