blob: 169d9ecb459605730edea2ddb0ebb64b94841ca7 [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.config;
import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.proto.Entities.UserPreferences;
import com.google.gerrit.server.cache.proto.Cache.CachedPreferencesProto;
import java.util.Optional;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
/**
* Container class for preferences serialized as Git-style config files. Keeps the values as {@link
* CachedPreferencesProto}s as they are immutable and thread-safe.
*
* <p>The config string wrapped by this class might represent different structures. See {@link
* CachedPreferencesProto} for more details.
*/
@AutoValue
public abstract class CachedPreferences {
public static final CachedPreferences EMPTY =
fromCachedPreferencesProto(CachedPreferencesProto.getDefaultInstance());
protected abstract CachedPreferencesProto config();
public Optional<CachedPreferencesProto> nonEmptyConfig() {
return config().equals(EMPTY.config()) ? Optional.empty() : Optional.of(config());
}
/** Returns a cache-able representation of the preferences proto. */
public static CachedPreferences fromUserPreferencesProto(UserPreferences proto) {
return fromCachedPreferencesProto(
CachedPreferencesProto.newBuilder().setUserPreferences(proto).build());
}
/** Returns a cache-able representation of the git config. */
public static CachedPreferences fromLegacyConfig(Config cfg) {
return fromCachedPreferencesProto(
CachedPreferencesProto.newBuilder().setLegacyGitConfig(cfg.toText()).build());
}
/** Returns a cache-able representation of the preferences proto. */
public static CachedPreferences fromCachedPreferencesProto(
@Nullable CachedPreferencesProto proto) {
if (proto != null) {
return new AutoValue_CachedPreferences(proto);
}
return EMPTY;
}
public static GeneralPreferencesInfo general(
Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
return getPreferences(
defaultPreferences,
userPreferences,
PreferencesParserUtil::parseGeneralPreferences,
p ->
UserPreferencesConverter.GeneralPreferencesInfoConverter.fromProto(
p.getGeneralPreferencesInfo()),
GeneralPreferencesInfo.defaults());
}
public static DiffPreferencesInfo diff(
Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
return getPreferences(
defaultPreferences,
userPreferences,
PreferencesParserUtil::parseDiffPreferences,
p ->
UserPreferencesConverter.DiffPreferencesInfoConverter.fromProto(
p.getDiffPreferencesInfo()),
DiffPreferencesInfo.defaults());
}
public static EditPreferencesInfo edit(
Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
return getPreferences(
defaultPreferences,
userPreferences,
PreferencesParserUtil::parseEditPreferences,
p ->
UserPreferencesConverter.EditPreferencesInfoConverter.fromProto(
p.getEditPreferencesInfo()),
EditPreferencesInfo.defaults());
}
public Config asConfig() {
try {
switch (config().getPreferencesCase()) {
case LEGACY_GIT_CONFIG:
// continue below
case PREFERENCES_NOT_SET:
Config cfg = new Config();
cfg.fromText(config().getLegacyGitConfig());
return cfg;
case USER_PREFERENCES:
break;
}
} catch (ConfigInvalidException e) {
throw new StorageException(e);
}
throw new StorageException(
String.format(
"Cannot parse the given config as a CachedPreferencesProto proto. Got [%s]", config()));
}
public UserPreferences asUserPreferencesProto() {
if (config().hasUserPreferences()) {
return config().getUserPreferences();
}
throw new StorageException(
String.format(
"Cannot parse the given config as a UserPreferences proto. Got [%s]", config()));
}
@Nullable
private static Config configOrNull(Optional<CachedPreferences> cachedPreferences) {
return cachedPreferences.map(CachedPreferences::asConfig).orElse(null);
}
@FunctionalInterface
private interface ComputePreferencesFn<PreferencesT> {
PreferencesT apply(Config cfg, @Nullable Config defaultCfg, @Nullable PreferencesT input)
throws ConfigInvalidException;
}
private static <PreferencesT> PreferencesT getPreferences(
Optional<CachedPreferences> defaultPreferences,
CachedPreferences userPreferences,
ComputePreferencesFn<PreferencesT> computePreferencesFn,
Function<UserPreferences, PreferencesT> fromUserPreferencesFn,
PreferencesT javaDefaults) {
try {
CachedPreferencesProto userPreferencesProto = userPreferences.config();
switch (userPreferencesProto.getPreferencesCase()) {
case USER_PREFERENCES:
PreferencesT pref =
fromUserPreferencesFn.apply(userPreferencesProto.getUserPreferences());
return computePreferencesFn.apply(new Config(), configOrNull(defaultPreferences), pref);
case LEGACY_GIT_CONFIG:
return computePreferencesFn.apply(
userPreferences.asConfig(), configOrNull(defaultPreferences), null);
case PREFERENCES_NOT_SET:
throw new ConfigInvalidException("Invalid config " + userPreferences);
}
} catch (ConfigInvalidException e) {
return javaDefaults;
}
return javaDefaults;
}
}