All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cloud.prefab.client.internal.UpdatingConfigResolver Maven / Gradle / Ivy

Go to download

API Client for https://prefab.cloud: rate limits, feature flags and semaphores as a service

There is a newer version: 0.3.23
Show newest version
package cloud.prefab.client.internal;

import static cloud.prefab.client.config.logging.AbstractLoggingListener.LOG_LEVEL_PREFIX;

import cloud.prefab.client.ConfigClient;
import cloud.prefab.client.config.ConfigChangeEvent;
import cloud.prefab.client.config.Match;
import cloud.prefab.client.config.Provenance;
import cloud.prefab.client.config.logging.LogLevelChangeEvent;
import cloud.prefab.client.exceptions.ConfigValueException;
import cloud.prefab.context.PrefabContextSetReadable;
import cloud.prefab.domain.Prefab;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UpdatingConfigResolver {

  private static final Logger LOG = LoggerFactory.getLogger(UpdatingConfigResolver.class);

  private final ConfigLoader configLoader;
  private final ConfigStoreConfigValueDeltaCalculator configStoreConfigValueDeltaCalculator;

  private final ConfigStoreImpl configStore;
  private final ConfigResolver configResolver;
  private final AbstractConfigStoreDeltaCalculator logLevelValueDeltaCalculator;

  public UpdatingConfigResolver(
    ConfigLoader configLoader,
    WeightedValueEvaluator weightedValueEvaluator,
    ConfigStoreConfigValueDeltaCalculator configStoreConfigValueDeltaCalculator
  ) {
    this.configLoader = configLoader;
    this.configStoreConfigValueDeltaCalculator = configStoreConfigValueDeltaCalculator;
    this.logLevelValueDeltaCalculator =
      new AbstractConfigStoreDeltaCalculator<>() {
        @Override
        LogLevelChangeEvent createEvent(
          String name,
          Optional oldValue,
          Optional newValue
        ) {
          return new LogLevelChangeEvent(name, oldValue, newValue);
        }
      };
    this.configStore = new ConfigStoreImpl();
    ConfigRuleEvaluator configRuleEvaluator = new ConfigRuleEvaluator(
      configStore,
      weightedValueEvaluator
    );
    this.configResolver =
      new ConfigResolver(configStore, configRuleEvaluator, new SystemEnvVarLookup());
  }

  /**
   * Return the changed config values since last update()
   */

  public static class ChangeLists {

    final List configChangeEvents;

    final List logLevelChangeEvents;

    public ChangeLists(
      List configChangeEvents,
      List logLevelChangeEvents
    ) {
      this.configChangeEvents = configChangeEvents;
      this.logLevelChangeEvents = logLevelChangeEvents;
    }

    public List getConfigChangeEvents() {
      return configChangeEvents;
    }

    public List getLogLevelChangeEvents() {
      return logLevelChangeEvents;
    }
  }

  public ChangeLists update() {
    // catch exceptions resolving, treat as absent
    // store the old map
    Map before = buildConfigByNameMap();
    Map logLevelsBefore = buildLogLevelValueMap();

    // load the new map
    makeLocal();

    // build the new map
    Map after = buildConfigByNameMap();
    Map logLevelsAfter = buildLogLevelValueMap();

    return new ChangeLists(
      configStoreConfigValueDeltaCalculator.computeChangeEvents(before, after),
      logLevelValueDeltaCalculator.computeChangeEvents(logLevelsBefore, logLevelsAfter)
    );
  }

  private Map buildLogLevelValueMap() {
    return configStore
      .entrySet()
      .stream()
      .filter(stringConfigElementEntry ->
        stringConfigElementEntry.getValue().getConfig().getConfigType() ==
        Prefab.ConfigType.LOG_LEVEL ||
        stringConfigElementEntry.getKey().startsWith(LOG_LEVEL_PREFIX)
      )
      .map(entry ->
        safeResolve(entry.getKey())
          .map(cv -> Maps.immutableEntry(entry.getKey(), cv))
          .filter(resolvedEntry -> resolvedEntry.getValue().hasLogLevel())
      )
      .flatMap(Optional::stream)
      .collect(
        Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getLogLevel())
      );
  }

  private Map buildConfigByNameMap() {
    return configStore
      .entrySet()
      .stream()
      .map(entry -> Maps.immutableEntry(entry.getKey(), entry.getValue().getConfig()))
      .filter(entry -> entry.getValue().getRowsCount() > 0)
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
  }

  private Optional safeResolve(String key) {
    try {
      return configResolver.getConfigValue(key);
    } catch (ConfigValueException configValueException) {
      LOG.warn("error evaluating config {} ", key, configValueException);
      return Optional.empty();
    }
  }

  public long getHighwaterMark() {
    return configLoader.getHighwaterMark();
  }

  public synchronized void loadConfigsFromLocalFile() {
    loadConfigs(configLoader.loadFromJsonFile(), ConfigClient.Source.LOCAL_FILE);
  }

  public synchronized void loadConfigs(
    Prefab.Configs configs,
    ConfigClient.Source source
  ) {
    final long startingHighWaterMark = configLoader.getHighwaterMark();
    Provenance provenance = new Provenance(source);
    configLoader.setConfigs(configs, provenance);
    if (configLoader.getHighwaterMark() > startingHighWaterMark) {
      LOG.info(
        "Found new checkpoint with highwater id {} from {} in project {} environment: {} with {} configs",
        configLoader.getHighwaterMark(),
        provenance,
        configs.getConfigServicePointer().getProjectId(),
        configs.getConfigServicePointer().getProjectEnvId(),
        configs.getConfigsCount()
      );
    } else {
      LOG.debug(
        "Checkpoint with highwater with highwater id {} from {}. No changes.",
        configLoader.getHighwaterMark(),
        provenance.getSource()
      );
    }
  }

  /**
   * set the localMap
   */
  private void makeLocal() {
    configStore.set(configLoader.calcConfig());
  }

  public String contentsString() {
    return configResolver.contentsString();
  }

  public Collection getKeys() {
    return configResolver.getKeys();
  }

  public boolean containsKey(String key) {
    return configResolver.containsKey(key);
  }

  public Optional getConfigValue(
    String key,
    LookupContext lookupContext
  ) {
    return configResolver.getConfigValue(key, lookupContext);
  }

  public Optional getConfigValue(String key) {
    return configResolver.getConfigValue(key);
  }

  public ConfigResolver getResolver() {
    return configResolver;
  }

  public Optional getMatch(String key, LookupContext lookupContext) {
    return configResolver.getMatch(key, lookupContext);
  }

  public Optional getRawMatch(String key, LookupContext lookupContext) {
    return configResolver.getRawMatch(key, lookupContext);
  }

  public PrefabContextSetReadable getApiDefaultContext() {
    return configStore.getConfigIncludedContext();
  }

  public PrefabContextSetReadable getGlobalContext() {
    return configStore.getGlobalContext();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy