LCOV - code coverage report
Current view: top level - server/config - ProjectConfigEntry.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 62 111 55.9 %
Date: 2022-11-19 15:00:39 Functions: 21 41 51.2 %

          Line data    Source code
       1             : // Copyright (C) 2013 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.config;
      16             : 
      17             : import static java.util.stream.Collectors.toList;
      18             : 
      19             : import com.google.common.flogger.FluentLogger;
      20             : import com.google.gerrit.common.Nullable;
      21             : import com.google.gerrit.entities.Project;
      22             : import com.google.gerrit.entities.RefNames;
      23             : import com.google.gerrit.extensions.annotations.ExtensionPoint;
      24             : import com.google.gerrit.extensions.api.projects.ConfigValue;
      25             : import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
      26             : import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
      27             : import com.google.gerrit.extensions.registration.DynamicMap;
      28             : import com.google.gerrit.extensions.registration.Extension;
      29             : import com.google.gerrit.server.git.GitRepositoryManager;
      30             : import com.google.gerrit.server.project.ProjectConfig;
      31             : import com.google.gerrit.server.project.ProjectState;
      32             : import com.google.inject.Inject;
      33             : import com.google.inject.ProvisionException;
      34             : import java.io.IOException;
      35             : import java.util.Arrays;
      36             : import java.util.List;
      37             : import org.eclipse.jgit.errors.ConfigInvalidException;
      38             : import org.eclipse.jgit.errors.RepositoryNotFoundException;
      39             : import org.eclipse.jgit.lib.ObjectId;
      40             : import org.eclipse.jgit.lib.Repository;
      41             : 
      42             : @ExtensionPoint
      43             : public class ProjectConfigEntry {
      44             :   private final String displayName;
      45             :   private final String description;
      46             :   private final boolean inheritable;
      47             :   private final String defaultValue;
      48             :   private final ProjectConfigEntryType type;
      49             :   private final List<String> permittedValues;
      50             : 
      51             :   public ProjectConfigEntry(String displayName, String defaultValue) {
      52           1 :     this(displayName, defaultValue, false);
      53           1 :   }
      54             : 
      55             :   public ProjectConfigEntry(String displayName, String defaultValue, boolean inheritable) {
      56           1 :     this(displayName, defaultValue, inheritable, null);
      57           1 :   }
      58             : 
      59             :   public ProjectConfigEntry(
      60             :       String displayName, String defaultValue, boolean inheritable, String description) {
      61           1 :     this(displayName, defaultValue, ProjectConfigEntryType.STRING, null, inheritable, description);
      62           1 :   }
      63             : 
      64             :   public ProjectConfigEntry(String displayName, int defaultValue) {
      65           0 :     this(displayName, defaultValue, false);
      66           0 :   }
      67             : 
      68             :   public ProjectConfigEntry(String displayName, int defaultValue, boolean inheritable) {
      69           0 :     this(displayName, defaultValue, inheritable, null);
      70           0 :   }
      71             : 
      72             :   public ProjectConfigEntry(
      73             :       String displayName, int defaultValue, boolean inheritable, String description) {
      74           0 :     this(
      75             :         displayName,
      76           0 :         Integer.toString(defaultValue),
      77             :         ProjectConfigEntryType.INT,
      78             :         null,
      79             :         inheritable,
      80             :         description);
      81           0 :   }
      82             : 
      83             :   public ProjectConfigEntry(String displayName, long defaultValue) {
      84           0 :     this(displayName, defaultValue, false);
      85           0 :   }
      86             : 
      87             :   public ProjectConfigEntry(String displayName, long defaultValue, boolean inheritable) {
      88           0 :     this(displayName, defaultValue, inheritable, null);
      89           0 :   }
      90             : 
      91             :   public ProjectConfigEntry(
      92             :       String displayName, long defaultValue, boolean inheritable, String description) {
      93           0 :     this(
      94             :         displayName,
      95           0 :         Long.toString(defaultValue),
      96             :         ProjectConfigEntryType.LONG,
      97             :         null,
      98             :         inheritable,
      99             :         description);
     100           0 :   }
     101             : 
     102             :   // For inheritable boolean use 'LIST' type with InheritableBoolean
     103             :   public ProjectConfigEntry(String displayName, boolean defaultValue) {
     104           1 :     this(displayName, defaultValue, null);
     105           1 :   }
     106             : 
     107             :   // For inheritable boolean use 'LIST' type with InheritableBoolean
     108             :   public ProjectConfigEntry(String displayName, boolean defaultValue, String description) {
     109           1 :     this(
     110             :         displayName,
     111           1 :         Boolean.toString(defaultValue),
     112             :         ProjectConfigEntryType.BOOLEAN,
     113             :         null,
     114             :         false,
     115             :         description);
     116           1 :   }
     117             : 
     118             :   public ProjectConfigEntry(String displayName, String defaultValue, List<String> permittedValues) {
     119           0 :     this(displayName, defaultValue, permittedValues, false);
     120           0 :   }
     121             : 
     122             :   public ProjectConfigEntry(
     123             :       String displayName, String defaultValue, List<String> permittedValues, boolean inheritable) {
     124           0 :     this(displayName, defaultValue, permittedValues, inheritable, null);
     125           0 :   }
     126             : 
     127             :   public ProjectConfigEntry(
     128             :       String displayName,
     129             :       String defaultValue,
     130             :       List<String> permittedValues,
     131             :       boolean inheritable,
     132             :       String description) {
     133           0 :     this(
     134             :         displayName,
     135             :         defaultValue,
     136             :         ProjectConfigEntryType.LIST,
     137             :         permittedValues,
     138             :         inheritable,
     139             :         description);
     140           0 :   }
     141             : 
     142             :   public <T extends Enum<?>> ProjectConfigEntry(
     143             :       String displayName, T defaultValue, Class<T> permittedValues) {
     144           0 :     this(displayName, defaultValue, permittedValues, false);
     145           0 :   }
     146             : 
     147             :   public <T extends Enum<?>> ProjectConfigEntry(
     148             :       String displayName, T defaultValue, Class<T> permittedValues, boolean inheritable) {
     149           0 :     this(displayName, defaultValue, permittedValues, inheritable, null);
     150           0 :   }
     151             : 
     152             :   public <T extends Enum<?>> ProjectConfigEntry(
     153             :       String displayName,
     154             :       T defaultValue,
     155             :       Class<T> permittedValues,
     156             :       boolean inheritable,
     157             :       String description) {
     158           0 :     this(
     159             :         displayName,
     160           0 :         defaultValue.name(),
     161             :         ProjectConfigEntryType.LIST,
     162           0 :         Arrays.stream(permittedValues.getEnumConstants()).map(Enum::name).collect(toList()),
     163             :         inheritable,
     164             :         description);
     165           0 :   }
     166             : 
     167             :   public ProjectConfigEntry(
     168             :       String displayName,
     169             :       String defaultValue,
     170             :       ProjectConfigEntryType type,
     171             :       List<String> permittedValues,
     172             :       boolean inheritable,
     173           1 :       String description) {
     174           1 :     this.displayName = displayName;
     175           1 :     this.defaultValue = defaultValue;
     176           1 :     this.type = type;
     177           1 :     this.permittedValues = permittedValues;
     178           1 :     this.inheritable = inheritable;
     179           1 :     this.description = description;
     180           1 :     if (type == ProjectConfigEntryType.ARRAY && inheritable) {
     181           0 :       throw new ProvisionException("ARRAY doesn't support inheritable values");
     182             :     }
     183           1 :   }
     184             : 
     185             :   public String getDisplayName() {
     186           1 :     return displayName;
     187             :   }
     188             : 
     189             :   public String getDescription() {
     190           1 :     return description;
     191             :   }
     192             : 
     193             :   public boolean isInheritable() {
     194           1 :     return inheritable;
     195             :   }
     196             : 
     197             :   public String getDefaultValue() {
     198           1 :     return defaultValue;
     199             :   }
     200             : 
     201             :   public ProjectConfigEntryType getType() {
     202           1 :     return type;
     203             :   }
     204             : 
     205             :   public List<String> getPermittedValues() {
     206           1 :     return permittedValues;
     207             :   }
     208             : 
     209             :   /**
     210             :    * Returns whether the project is editable
     211             :    *
     212             :    * @param project project state.
     213             :    */
     214             :   public boolean isEditable(ProjectState project) {
     215           1 :     return true;
     216             :   }
     217             : 
     218             :   /**
     219             :    * Returns any warning associated with the project
     220             :    *
     221             :    * @param project project state.
     222             :    */
     223             :   public String getWarning(ProjectState project) {
     224           1 :     return null;
     225             :   }
     226             : 
     227             :   /**
     228             :    * Called before the project config is updated. To modify the value before the project config is
     229             :    * updated, override this method and return the modified value. Default implementation returns the
     230             :    * same value.
     231             :    *
     232             :    * @param configValue the original configValue that was entered.
     233             :    * @return the modified configValue.
     234             :    */
     235             :   public ConfigValue preUpdate(ConfigValue configValue) {
     236           1 :     return configValue;
     237             :   }
     238             : 
     239             :   /**
     240             :    * Called after reading the project config value. To modify the value before returning it to the
     241             :    * client, override this method and return the modified value. Default implementation returns the
     242             :    * same value.
     243             :    *
     244             :    * @param project the project.
     245             :    * @param value the actual value of the config entry (computed out of the configured value, the
     246             :    *     inherited value and the default value).
     247             :    * @return the modified value.
     248             :    */
     249             :   public String onRead(ProjectState project, String value) {
     250           1 :     return value;
     251             :   }
     252             : 
     253             :   /**
     254             :    * Called after reading the project config value of type ARRAY. To modify the values before
     255             :    * returning it to the client, override this method and return the modified values. Default
     256             :    * implementation returns the same values.
     257             :    *
     258             :    * @param project the project.
     259             :    * @param values the actual values of the config entry (computed out of the configured value, the
     260             :    *     inherited value and the default value).
     261             :    * @return the modified values.
     262             :    */
     263             :   public List<String> onRead(ProjectState project, List<String> values) {
     264           0 :     return values;
     265             :   }
     266             : 
     267             :   /**
     268             :    * Called after a project config is updated.
     269             :    *
     270             :    * @param project project name.
     271             :    * @param oldValue old entry value.
     272             :    * @param newValue new entry value.
     273             :    */
     274           0 :   public void onUpdate(Project.NameKey project, String oldValue, String newValue) {}
     275             : 
     276             :   /**
     277             :    * Called after a project config is updated.
     278             :    *
     279             :    * @param project project name.
     280             :    * @param oldValue old entry value.
     281             :    * @param newValue new entry value.
     282             :    */
     283           0 :   public void onUpdate(Project.NameKey project, Boolean oldValue, Boolean newValue) {}
     284             : 
     285             :   /**
     286             :    * Called after a project config is updated.
     287             :    *
     288             :    * @param project project name.
     289             :    * @param oldValue old entry value.
     290             :    * @param newValue new entry value.
     291             :    */
     292           0 :   public void onUpdate(Project.NameKey project, Integer oldValue, Integer newValue) {}
     293             : 
     294             :   /**
     295             :    * Called after a project config is updated.
     296             :    *
     297             :    * @param project project name.
     298             :    * @param oldValue old entry value.
     299             :    * @param newValue new entry value.
     300             :    */
     301           0 :   public void onUpdate(Project.NameKey project, Long oldValue, Long newValue) {}
     302             : 
     303             :   public static class UpdateChecker implements GitReferenceUpdatedListener {
     304         151 :     private static final FluentLogger logger = FluentLogger.forEnclosingClass();
     305             : 
     306             :     private final GitRepositoryManager repoManager;
     307             :     private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
     308             :     private final ProjectConfig.Factory projectConfigFactory;
     309             : 
     310             :     @Inject
     311             :     UpdateChecker(
     312             :         GitRepositoryManager repoManager,
     313             :         DynamicMap<ProjectConfigEntry> pluginConfigEntries,
     314         151 :         ProjectConfig.Factory projectConfigFactory) {
     315         151 :       this.repoManager = repoManager;
     316         151 :       this.pluginConfigEntries = pluginConfigEntries;
     317         151 :       this.projectConfigFactory = projectConfigFactory;
     318         151 :     }
     319             : 
     320             :     @Override
     321             :     public void onGitReferenceUpdated(Event event) {
     322         151 :       Project.NameKey p = Project.nameKey(event.getProjectName());
     323         151 :       if (!event.getRefName().equals(RefNames.REFS_CONFIG)) {
     324         151 :         return;
     325             :       }
     326             : 
     327             :       try {
     328         144 :         ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId());
     329         144 :         ProjectConfig newCfg = parseConfig(p, event.getNewObjectId());
     330         144 :         if (oldCfg != null && newCfg != null) {
     331          93 :           for (Extension<ProjectConfigEntry> e : pluginConfigEntries) {
     332           1 :             ProjectConfigEntry configEntry = e.getProvider().get();
     333           1 :             String newValue = getValue(newCfg, e);
     334           1 :             String oldValue = getValue(oldCfg, e);
     335           1 :             if ((newValue == null && oldValue == null)
     336           1 :                 || (newValue != null && newValue.equals(oldValue))) {
     337           1 :               return;
     338             :             }
     339             : 
     340           0 :             switch (configEntry.getType()) {
     341             :               case BOOLEAN:
     342           0 :                 configEntry.onUpdate(p, toBoolean(oldValue), toBoolean(newValue));
     343           0 :                 break;
     344             :               case INT:
     345           0 :                 configEntry.onUpdate(p, toInt(oldValue), toInt(newValue));
     346           0 :                 break;
     347             :               case LONG:
     348           0 :                 configEntry.onUpdate(p, toLong(oldValue), toLong(newValue));
     349           0 :                 break;
     350             :               case LIST:
     351             :               case STRING:
     352             :               case ARRAY:
     353             :               default:
     354           0 :                 configEntry.onUpdate(p, oldValue, newValue);
     355             :             }
     356           0 :           }
     357             :         }
     358           0 :       } catch (IOException | ConfigInvalidException e) {
     359           0 :         logger.atSevere().withCause(e).log(
     360           0 :             "Failed to check if plugin config of project %s was updated.", p.get());
     361         144 :       }
     362         144 :     }
     363             : 
     364             :     @Nullable
     365             :     private ProjectConfig parseConfig(Project.NameKey p, String idStr)
     366             :         throws IOException, ConfigInvalidException, RepositoryNotFoundException {
     367         144 :       ObjectId id = ObjectId.fromString(idStr);
     368         144 :       if (ObjectId.zeroId().equals(id)) {
     369         144 :         return null;
     370             :       }
     371         144 :       try (Repository repo = repoManager.openRepository(p)) {
     372         144 :         ProjectConfig pc = projectConfigFactory.create(p);
     373         144 :         pc.load(repo, id);
     374         144 :         return pc;
     375             :       }
     376             :     }
     377             : 
     378             :     private static String getValue(ProjectConfig cfg, Extension<ProjectConfigEntry> e) {
     379           1 :       String value = cfg.getPluginConfig(e.getPluginName()).getString(e.getExportName());
     380           1 :       if (value == null) {
     381           1 :         value = e.getProvider().get().getDefaultValue();
     382             :       }
     383           1 :       return value;
     384             :     }
     385             :   }
     386             : 
     387             :   @Nullable
     388             :   private static Boolean toBoolean(String value) {
     389           0 :     return value != null ? Boolean.parseBoolean(value) : null;
     390             :   }
     391             : 
     392             :   @Nullable
     393             :   private static Integer toInt(String value) {
     394           0 :     return value != null ? Integer.parseInt(value) : null;
     395             :   }
     396             : 
     397             :   @Nullable
     398             :   private static Long toLong(String value) {
     399           0 :     return value != null ? Long.parseLong(value) : null;
     400             :   }
     401             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750