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); } } }
/// <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)); }
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; }