Merge parent config values with child values
ProjectLevelConfig.getWithInheritance returns a config where values defined in the child config will replace any matching value in the parent config. This change adds a boolean merge parameter to ProjectLevelConfig.getWithInheritance that will allow the caller to generate a config where the values from the parent config are merged with the values from the child project. Bug: Issue 8468 Change-Id: I5edacfacb37b7ee525b7c4e5340b97a62c4dc243
This commit is contained in:
@@ -22,6 +22,7 @@ import com.google.gerrit.acceptance.PushOneCommit;
|
|||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
|
import java.util.Arrays;
|
||||||
import org.eclipse.jgit.junit.TestRepository;
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -114,4 +115,67 @@ public class ProjectLevelConfigIT extends AbstractDaemonTest {
|
|||||||
|
|
||||||
assertThat(state.getConfig(configName).get().toText()).isEqualTo(cfg.toText());
|
assertThat(state.getConfig(configName).get().toText()).isEqualTo(cfg.toText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withMergedInheritance() throws Exception {
|
||||||
|
String configName = "test.config";
|
||||||
|
|
||||||
|
Config parentCfg = new Config();
|
||||||
|
parentCfg.setString("s1", null, "k1", "parentValue1");
|
||||||
|
parentCfg.setString("s1", null, "k2", "parentValue2");
|
||||||
|
parentCfg.setString("s2", "ss", "k3", "parentValue3");
|
||||||
|
parentCfg.setString("s2", "ss", "k4", "parentValue4");
|
||||||
|
|
||||||
|
pushFactory
|
||||||
|
.create(
|
||||||
|
db,
|
||||||
|
admin.getIdent(),
|
||||||
|
testRepo,
|
||||||
|
"Create Project Level Config",
|
||||||
|
configName,
|
||||||
|
parentCfg.toText())
|
||||||
|
.to(RefNames.REFS_CONFIG)
|
||||||
|
.assertOkStatus();
|
||||||
|
|
||||||
|
Project.NameKey childProject = createProject("child", project);
|
||||||
|
TestRepository<?> childTestRepo = cloneProject(childProject);
|
||||||
|
fetch(childTestRepo, RefNames.REFS_CONFIG + ":refs/heads/config");
|
||||||
|
childTestRepo.reset("refs/heads/config");
|
||||||
|
|
||||||
|
Config cfg = new Config();
|
||||||
|
cfg.setString("s1", null, "k1", "parentValue1");
|
||||||
|
cfg.setString("s1", null, "k2", "parentValue2");
|
||||||
|
cfg.setString("s2", "ss", "k3", "parentValue3");
|
||||||
|
cfg.setString("s2", "ss", "k4", "parentValue4");
|
||||||
|
cfg.setString("s1", null, "k1", "childValue1");
|
||||||
|
cfg.setString("s2", "ss", "k3", "childValue2");
|
||||||
|
cfg.setString("s3", null, "k5", "childValue3");
|
||||||
|
cfg.setString("s3", "ss", "k6", "childValue4");
|
||||||
|
|
||||||
|
pushFactory
|
||||||
|
.create(
|
||||||
|
db,
|
||||||
|
admin.getIdent(),
|
||||||
|
childTestRepo,
|
||||||
|
"Create Project Level Config",
|
||||||
|
configName,
|
||||||
|
cfg.toText())
|
||||||
|
.to(RefNames.REFS_CONFIG)
|
||||||
|
.assertOkStatus();
|
||||||
|
|
||||||
|
ProjectState state = projectCache.get(childProject);
|
||||||
|
|
||||||
|
Config expectedCfg = new Config();
|
||||||
|
expectedCfg.setStringList("s1", null, "k1", Arrays.asList("childValue1", "parentValue1"));
|
||||||
|
expectedCfg.setString("s1", null, "k2", "parentValue2");
|
||||||
|
expectedCfg.setStringList("s2", "ss", "k3", Arrays.asList("childValue2", "parentValue3"));
|
||||||
|
expectedCfg.setString("s2", "ss", "k4", "parentValue4");
|
||||||
|
expectedCfg.setString("s3", null, "k5", "childValue3");
|
||||||
|
expectedCfg.setString("s3", "ss", "k6", "childValue4");
|
||||||
|
|
||||||
|
assertThat(state.getConfig(configName).getWithInheritance(true).toText())
|
||||||
|
.isEqualTo(expectedCfg.toText());
|
||||||
|
|
||||||
|
assertThat(state.getConfig(configName).get().toText()).isEqualTo(cfg.toText());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,7 +288,32 @@ public class PluginConfigFactory implements ReloadPluginListener {
|
|||||||
*/
|
*/
|
||||||
public Config getProjectPluginConfigWithInheritance(
|
public Config getProjectPluginConfigWithInheritance(
|
||||||
Project.NameKey projectName, String pluginName) throws NoSuchProjectException {
|
Project.NameKey projectName, String pluginName) throws NoSuchProjectException {
|
||||||
return getPluginConfig(projectName, pluginName).getWithInheritance();
|
return getPluginConfig(projectName, pluginName).getWithInheritance(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration for the specified plugin that is stored in the '{@code
|
||||||
|
* <plugin-name>.config}' file in the 'refs/meta/config' branch of the specified project.
|
||||||
|
* Parameters from the '{@code <plugin-name>.config}' of the parent project are appended to this
|
||||||
|
* project's '{@code <plugin-name>.config}' files.
|
||||||
|
*
|
||||||
|
* <p>E.g.: child project: [mySection "mySubsection"] myKey = childValue
|
||||||
|
*
|
||||||
|
* <p>parent project: [mySection "mySubsection"] myKey = parentValue anotherKey = someValue
|
||||||
|
*
|
||||||
|
* <p>return: [mySection "mySubsection"] myKey = childValue myKey = parentValue anotherKey =
|
||||||
|
* someValue
|
||||||
|
*
|
||||||
|
* @param projectName the name of the project for which the plugin configuration should be
|
||||||
|
* returned
|
||||||
|
* @param pluginName the name of the plugin for which the configuration should be returned
|
||||||
|
* @return the plugin configuration from the '{@code <plugin-name>.config}' file of the specified
|
||||||
|
* project with parameters from the parent projects appended to the project values
|
||||||
|
* @throws NoSuchProjectException thrown if the specified project does not exist
|
||||||
|
*/
|
||||||
|
public Config getProjectPluginConfigWithMergedInheritance(
|
||||||
|
Project.NameKey projectName, String pluginName) throws NoSuchProjectException {
|
||||||
|
return getPluginConfig(projectName, pluginName).getWithInheritance(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -310,7 +335,30 @@ public class PluginConfigFactory implements ReloadPluginListener {
|
|||||||
*/
|
*/
|
||||||
public Config getProjectPluginConfigWithInheritance(
|
public Config getProjectPluginConfigWithInheritance(
|
||||||
ProjectState projectState, String pluginName) {
|
ProjectState projectState, String pluginName) {
|
||||||
return projectState.getConfig(pluginName + EXTENSION).getWithInheritance();
|
return projectState.getConfig(pluginName + EXTENSION).getWithInheritance(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration for the specified plugin that is stored in the '{@code
|
||||||
|
* <plugin-name>.config}' file in the 'refs/meta/config' branch of the specified project.
|
||||||
|
* Parameters from the '{@code <plugin-name>.config}' of the parent project are appended to this
|
||||||
|
* project's '{@code <plugin-name>.config}' files.
|
||||||
|
*
|
||||||
|
* <p>E.g.: child project: [mySection "mySubsection"] myKey = childValue
|
||||||
|
*
|
||||||
|
* <p>parent project: [mySection "mySubsection"] myKey = parentValue anotherKey = someValue
|
||||||
|
*
|
||||||
|
* <p>return: [mySection "mySubsection"] myKey = childValue myKey = parentValue anotherKey =
|
||||||
|
* someValue
|
||||||
|
*
|
||||||
|
* @param projectState the project for which the plugin configuration should be returned
|
||||||
|
* @param pluginName the name of the plugin for which the configuration should be returned
|
||||||
|
* @return the plugin configuration from the '{@code <plugin-name>.config}' file of the specified
|
||||||
|
* project with inheriting non-set parameters from the parent projects
|
||||||
|
*/
|
||||||
|
public Config getProjectPluginConfigWithMergedInheritance(
|
||||||
|
ProjectState projectState, String pluginName) {
|
||||||
|
return projectState.getConfig(pluginName + EXTENSION).getWithInheritance(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProjectLevelConfig getPluginConfig(Project.NameKey projectName, String pluginName)
|
private ProjectLevelConfig getPluginConfig(Project.NameKey projectName, String pluginName)
|
||||||
|
|||||||
@@ -15,11 +15,14 @@
|
|||||||
package com.google.gerrit.server.git;
|
package com.google.gerrit.server.git;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Streams;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
@@ -53,6 +56,22 @@ public class ProjectLevelConfig extends VersionedMetaData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Config getWithInheritance() {
|
public Config getWithInheritance() {
|
||||||
|
return getWithInheritance(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Config that includes the values from all parent projects.
|
||||||
|
*
|
||||||
|
* <p>Merging means that matching sections/subsection will be merged to include the values from
|
||||||
|
* both parent and child config.
|
||||||
|
*
|
||||||
|
* <p>No merging means that matching sections/subsections in the child project will replace the
|
||||||
|
* corresponding value from the parent.
|
||||||
|
*
|
||||||
|
* @param merge whether to merge parent values with child values or not.
|
||||||
|
* @return a combined config.
|
||||||
|
*/
|
||||||
|
public Config getWithInheritance(boolean merge) {
|
||||||
Config cfgWithInheritance = new Config();
|
Config cfgWithInheritance = new Config();
|
||||||
try {
|
try {
|
||||||
cfgWithInheritance.fromText(get().toText());
|
cfgWithInheritance.fromText(get().toText());
|
||||||
@@ -65,21 +84,41 @@ public class ProjectLevelConfig extends VersionedMetaData {
|
|||||||
for (String section : parentCfg.getSections()) {
|
for (String section : parentCfg.getSections()) {
|
||||||
Set<String> allNames = get().getNames(section);
|
Set<String> allNames = get().getNames(section);
|
||||||
for (String name : parentCfg.getNames(section)) {
|
for (String name : parentCfg.getNames(section)) {
|
||||||
|
String[] parentValues = parentCfg.getStringList(section, null, name);
|
||||||
if (!allNames.contains(name)) {
|
if (!allNames.contains(name)) {
|
||||||
|
cfgWithInheritance.setStringList(section, null, name, Arrays.asList(parentValues));
|
||||||
|
} else if (merge) {
|
||||||
cfgWithInheritance.setStringList(
|
cfgWithInheritance.setStringList(
|
||||||
section, null, name, Arrays.asList(parentCfg.getStringList(section, null, name)));
|
section,
|
||||||
|
null,
|
||||||
|
name,
|
||||||
|
Stream.concat(
|
||||||
|
Arrays.stream(cfg.getStringList(section, null, name)),
|
||||||
|
Arrays.stream(parentValues))
|
||||||
|
.sorted()
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String subsection : parentCfg.getSubsections(section)) {
|
for (String subsection : parentCfg.getSubsections(section)) {
|
||||||
allNames = get().getNames(section, subsection);
|
allNames = get().getNames(section, subsection);
|
||||||
for (String name : parentCfg.getNames(section, subsection)) {
|
for (String name : parentCfg.getNames(section, subsection)) {
|
||||||
|
String[] parentValues = parentCfg.getStringList(section, subsection, name);
|
||||||
if (!allNames.contains(name)) {
|
if (!allNames.contains(name)) {
|
||||||
|
cfgWithInheritance.setStringList(
|
||||||
|
section, subsection, name, Arrays.asList(parentValues));
|
||||||
|
} else if (merge) {
|
||||||
cfgWithInheritance.setStringList(
|
cfgWithInheritance.setStringList(
|
||||||
section,
|
section,
|
||||||
subsection,
|
subsection,
|
||||||
name,
|
name,
|
||||||
Arrays.asList(parentCfg.getStringList(section, subsection, name)));
|
Streams.concat(
|
||||||
|
Arrays.stream(cfg.getStringList(section, subsection, name)),
|
||||||
|
Arrays.stream(parentValues))
|
||||||
|
.sorted()
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user