Line data Source code
1 : // Copyright (C) 2020 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 com.google.common.base.Preconditions.checkState;
18 :
19 : import com.google.auto.value.AutoValue;
20 : import com.google.common.annotations.VisibleForTesting;
21 : import com.google.common.base.MoreObjects;
22 : import com.google.common.base.Strings;
23 : import com.google.common.collect.ImmutableMap;
24 : import com.google.common.collect.Iterables;
25 : import com.google.gerrit.common.Nullable;
26 : import com.google.gerrit.entities.AccountGroup;
27 : import com.google.gerrit.entities.CachedProjectConfig;
28 : import com.google.gerrit.entities.GroupReference;
29 : import com.google.gerrit.server.project.ProjectConfig;
30 : import com.google.gerrit.server.project.ProjectState;
31 : import java.util.Arrays;
32 : import java.util.HashMap;
33 : import java.util.List;
34 : import java.util.Map;
35 : import java.util.Optional;
36 : import java.util.Set;
37 : import org.eclipse.jgit.errors.ConfigInvalidException;
38 : import org.eclipse.jgit.lib.Config;
39 :
40 : @AutoValue
41 4 : public abstract class PluginConfig {
42 : private static final String PLUGIN = "plugin";
43 :
44 : protected abstract String pluginName();
45 :
46 : protected abstract Config cfg();
47 :
48 : protected abstract Optional<CachedProjectConfig> projectConfig();
49 :
50 : /** Mappings parsed from {@code groups} files. */
51 : protected abstract ImmutableMap<AccountGroup.UUID, GroupReference> groupReferences();
52 :
53 : public static PluginConfig create(
54 : String pluginName, Config cfg, @Nullable CachedProjectConfig projectConfig) {
55 : ImmutableMap.Builder<AccountGroup.UUID, GroupReference> groupReferences =
56 3 : ImmutableMap.builder();
57 3 : if (projectConfig != null) {
58 3 : groupReferences.putAll(projectConfig.getGroups());
59 : }
60 3 : return new AutoValue_PluginConfig(
61 3 : pluginName, copyConfig(cfg), Optional.ofNullable(projectConfig), groupReferences.build());
62 : }
63 :
64 : public static PluginConfig createFromGerritConfig(String pluginName, Config cfg) {
65 : // There is no need to make a defensive copy here because this value won't be cached.
66 : // gerrit.config uses baseConfig's (a member of Config) which would also make defensive copies
67 : // fail.
68 1 : return new AutoValue_PluginConfig(pluginName, cfg, Optional.empty(), ImmutableMap.of());
69 : }
70 :
71 : PluginConfig withInheritance(ProjectState.Factory projectStateFactory) {
72 1 : checkState(projectConfig().isPresent(), "no project config provided");
73 :
74 1 : ProjectState state = projectStateFactory.create(projectConfig().get());
75 1 : ProjectState parent = Iterables.getFirst(state.parents(), null);
76 1 : if (parent == null) {
77 1 : return this;
78 : }
79 :
80 1 : Map<AccountGroup.UUID, GroupReference> groupReferences = new HashMap<>();
81 1 : groupReferences.putAll(groupReferences());
82 1 : PluginConfig parentPluginConfig =
83 1 : parent.getPluginConfig(pluginName()).withInheritance(projectStateFactory);
84 1 : Set<String> allNames = cfg().getNames(PLUGIN, pluginName());
85 1 : Config newCfg = copyConfig(cfg());
86 1 : for (String name : parentPluginConfig.cfg().getNames(PLUGIN, pluginName())) {
87 1 : if (!allNames.contains(name)) {
88 1 : List<String> values =
89 1 : Arrays.asList(parentPluginConfig.cfg().getStringList(PLUGIN, pluginName(), name));
90 1 : for (String value : values) {
91 1 : Optional<GroupReference> groupRef =
92 : parentPluginConfig
93 1 : .projectConfig()
94 1 : .get()
95 1 : .getGroupByName(GroupReference.extractGroupName(value));
96 1 : if (groupRef.isPresent()) {
97 1 : groupReferences.putIfAbsent(groupRef.get().getUUID(), groupRef.get());
98 : }
99 1 : }
100 1 : newCfg.setStringList(PLUGIN, pluginName(), name, values);
101 : }
102 1 : }
103 1 : return new AutoValue_PluginConfig(
104 1 : pluginName(), newCfg, projectConfig(), ImmutableMap.copyOf(groupReferences));
105 : }
106 :
107 : private static Config copyConfig(Config cfg) {
108 3 : Config copiedCfg = new Config();
109 : try {
110 3 : copiedCfg.fromText(cfg.toText());
111 0 : } catch (ConfigInvalidException e) {
112 : // cannot happen
113 0 : throw new IllegalStateException(e);
114 3 : }
115 3 : return copiedCfg;
116 : }
117 :
118 : public String getString(String name) {
119 3 : return cfg().getString(PLUGIN, pluginName(), name);
120 : }
121 :
122 : public String getString(String name, String defaultValue) {
123 1 : if (defaultValue == null) {
124 1 : return cfg().getString(PLUGIN, pluginName(), name);
125 : }
126 0 : return MoreObjects.firstNonNull(cfg().getString(PLUGIN, pluginName(), name), defaultValue);
127 : }
128 :
129 : public String[] getStringList(String name) {
130 1 : return cfg().getStringList(PLUGIN, pluginName(), name);
131 : }
132 :
133 : public int getInt(String name, int defaultValue) {
134 0 : return cfg().getInt(PLUGIN, pluginName(), name, defaultValue);
135 : }
136 :
137 : public long getLong(String name, long defaultValue) {
138 0 : return cfg().getLong(PLUGIN, pluginName(), name, defaultValue);
139 : }
140 :
141 : public boolean getBoolean(String name, boolean defaultValue) {
142 0 : return cfg().getBoolean(PLUGIN, pluginName(), name, defaultValue);
143 : }
144 :
145 : public <T extends Enum<?>> T getEnum(String name, T defaultValue) {
146 0 : return cfg().getEnum(PLUGIN, pluginName(), name, defaultValue);
147 : }
148 :
149 : public <T extends Enum<?>> T getEnum(T[] all, String name, T defaultValue) {
150 0 : return cfg().getEnum(all, PLUGIN, pluginName(), name, defaultValue);
151 : }
152 :
153 : public Set<String> getNames() {
154 1 : return cfg().getNames(PLUGIN, pluginName(), true);
155 : }
156 :
157 : public Optional<GroupReference> getGroupReference(String name) {
158 2 : String exactName = GroupReference.extractGroupName(getString(name));
159 2 : return groupReferences().values().stream().filter(g -> g.getName().equals(exactName)).findAny();
160 : }
161 :
162 : /** Mutable representation of {@link PluginConfig}. Used for updates. */
163 : public static class Update {
164 : private final String pluginName;
165 : private Config cfg;
166 : private final Optional<ProjectConfig> projectConfig;
167 :
168 3 : public Update(String pluginName, Config cfg, Optional<ProjectConfig> projectConfig) {
169 3 : this.pluginName = pluginName;
170 3 : this.cfg = cfg;
171 3 : this.projectConfig = projectConfig;
172 3 : }
173 :
174 : @VisibleForTesting
175 : public static Update forTest(String pluginName, Config cfg) {
176 0 : return new Update(pluginName, cfg, Optional.empty());
177 : }
178 :
179 : public PluginConfig asPluginConfig() {
180 0 : return PluginConfig.create(
181 0 : pluginName, cfg, projectConfig.map(ProjectConfig::getCacheable).orElse(null));
182 : }
183 :
184 : public String getString(String name) {
185 0 : return cfg.getString(PLUGIN, pluginName, name);
186 : }
187 :
188 : public String getString(String name, String defaultValue) {
189 0 : if (defaultValue == null) {
190 0 : return cfg.getString(PLUGIN, pluginName, name);
191 : }
192 0 : return MoreObjects.firstNonNull(cfg.getString(PLUGIN, pluginName, name), defaultValue);
193 : }
194 :
195 : public String[] getStringList(String name) {
196 0 : return cfg.getStringList(PLUGIN, pluginName, name);
197 : }
198 :
199 : public int getInt(String name, int defaultValue) {
200 0 : return cfg.getInt(PLUGIN, pluginName, name, defaultValue);
201 : }
202 :
203 : public long getLong(String name, long defaultValue) {
204 0 : return cfg.getLong(PLUGIN, pluginName, name, defaultValue);
205 : }
206 :
207 : public boolean getBoolean(String name, boolean defaultValue) {
208 0 : return cfg.getBoolean(PLUGIN, pluginName, name, defaultValue);
209 : }
210 :
211 : public <T extends Enum<?>> T getEnum(String name, T defaultValue) {
212 0 : return cfg.getEnum(PLUGIN, pluginName, name, defaultValue);
213 : }
214 :
215 : public <T extends Enum<?>> T getEnum(T[] all, String name, T defaultValue) {
216 0 : return cfg.getEnum(all, PLUGIN, pluginName, name, defaultValue);
217 : }
218 :
219 : public Set<String> getNames() {
220 0 : return cfg.getNames(PLUGIN, pluginName, true);
221 : }
222 :
223 : public void setString(String name, String value) {
224 2 : if (Strings.isNullOrEmpty(value)) {
225 0 : cfg.unset(PLUGIN, pluginName, name);
226 : } else {
227 2 : cfg.setString(PLUGIN, pluginName, name, value);
228 : }
229 2 : }
230 :
231 : public void setStringList(String name, List<String> values) {
232 1 : if (values == null || values.isEmpty()) {
233 0 : cfg.unset(PLUGIN, pluginName, name);
234 : } else {
235 1 : cfg.setStringList(PLUGIN, pluginName, name, values);
236 : }
237 1 : }
238 :
239 : public void setInt(String name, int value) {
240 0 : cfg.setInt(PLUGIN, pluginName, name, value);
241 0 : }
242 :
243 : public void setLong(String name, long value) {
244 0 : cfg.setLong(PLUGIN, pluginName, name, value);
245 0 : }
246 :
247 : public void setBoolean(String name, boolean value) {
248 1 : cfg.setBoolean(PLUGIN, pluginName, name, value);
249 1 : }
250 :
251 : public <T extends Enum<?>> void setEnum(String name, T value) {
252 0 : cfg.setEnum(PLUGIN, pluginName, name, value);
253 0 : }
254 :
255 : public void unset(String name) {
256 1 : cfg.unset(PLUGIN, pluginName, name);
257 1 : }
258 :
259 : public void setGroupReference(String name, GroupReference value) {
260 2 : checkState(projectConfig.isPresent(), "no project config provided");
261 2 : GroupReference groupRef = projectConfig.get().resolve(value);
262 2 : setString(name, groupRef.toConfigValue());
263 2 : }
264 : }
265 : }
|