]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4221 Make it possible for the Views and Developers plugin to define the differe...
authorJulien Lancelot <julien.lancelot@gmail.com>
Thu, 28 Mar 2013 10:45:29 +0000 (11:45 +0100)
committerJulien Lancelot <julien.lancelot@gmail.com>
Thu, 28 Mar 2013 10:45:29 +0000 (11:45 +0100)
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java
sonar-batch/src/main/java/org/sonar/batch/components/TimeMachineConfiguration.java
sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderTest.java
sonar-batch/src/test/java/org/sonar/batch/components/TimeMachineConfigurationTest.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceType.java
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypeTree.java
sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceTypes.java
sonar-plugin-api/src/test/java/org/sonar/api/resources/ResourceTypesTest.java

index 32f3875774b2ed8aeb79e39f64ccb017cdb227a2..e82ad5a9a8a228bd5fe92b60807e8f486cc78aae 100644 (file)
@@ -302,7 +302,7 @@ import java.util.List;
     defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3,
     category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
   @Property(
-    key = "sonar.timemachine.period4",
+    key = "sonar.timemachine.TRK.period4",
     name = "Period 4",
     description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
       "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
@@ -313,15 +313,17 @@ import java.util.List;
     project = true,
     global = false,
     defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4,
-    category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
+    category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS,
+    deprecatedKey = "sonar.timemachine.period4"),
   @Property(
-    key = "sonar.timemachine.period5",
+    key = "sonar.timemachine.TRK.period5",
     name = "Period 5",
     description = "See the property 'Period 4'",
     project = true,
     global = false,
     defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5,
-    category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
+    category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS,
+      deprecatedKey = "sonar.timemachine.period5"),
   @Property(
     key = CoreProperties.DRY_RUN,
     defaultValue = "false",
@@ -384,7 +386,13 @@ import java.util.List;
     defaultValue = "false",
     project = false,
     global = false,
-    type = PropertyType.BOOLEAN)
+    type = PropertyType.BOOLEAN),
+  @Property(
+    key = "sonar.enableFileVariation",
+    name = "Enable file variation",
+    global = false,
+    defaultValue = "false",
+    category = CoreProperties.CATEGORY_GENERAL),
 })
 public final class CorePlugin extends SonarPlugin {
 
index 7968679b26e1cd4859f2fa2cb45a1bc155dc8f11..f48d5847836e0733033bceebd84deb6d50939da4 100644 (file)
  */
 package org.sonar.batch.components;
 
-import org.apache.commons.configuration.Configuration;
 import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchExtension;
 import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
 import org.sonar.api.database.model.Snapshot;
 
 import java.text.ParseException;
@@ -32,9 +33,8 @@ import java.util.Date;
 
 public class PastSnapshotFinder implements BatchExtension {
 
-  /**
-   * IMPORTANT : please update default values in the ruby side too. See app/helpers/FiltersHelper.rb, method period_names()
-   */
+  private static final Logger LOG = LoggerFactory.getLogger(PastSnapshotFinder.class);
+
   private PastSnapshotFinderByDays finderByDays;
   private PastSnapshotFinderByVersion finderByVersion;
   private PastSnapshotFinderByDate finderByDate;
@@ -51,35 +51,25 @@ public class PastSnapshotFinder implements BatchExtension {
     this.finderByPreviousVersion = finderByPreviousVersion;
   }
 
-  public PastSnapshot find(Snapshot projectSnapshot, Configuration conf, int index) {
-    String propertyValue = getPropertyValue(conf, index);
+  public PastSnapshot find(Snapshot projectSnapshot, String rootQualifier, Settings settings, int index) {
+    String propertyValue = getPropertyValue(rootQualifier, settings, index);
     PastSnapshot pastSnapshot = find(projectSnapshot, index, propertyValue);
     if (pastSnapshot == null && StringUtils.isNotBlank(propertyValue)) {
-      LoggerFactory.getLogger(PastSnapshotFinder.class).debug("Property " + CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index + " is not valid: " + propertyValue);
+      LOG.debug("Property " + getProperty(rootQualifier, index) + " is not valid: " + propertyValue);
     }
     return pastSnapshot;
   }
 
-  static String getPropertyValue(Configuration conf, int index) {
-    String defaultValue = null;
-    switch (index) {
-      case 1:
-        defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1;
-        break;
-      case 2:
-        defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2;
-        break;
-      case 3:
-        defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3;
-        break;
-      case 4:
-        defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4;
-        break; // NOSONAR false-positive: constant 4 is the same than 5 (empty string)
-      case 5:
-        defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5;
-        break; // NOSONAR false-positive: constant 5 is the same than 4 (empty string)
+  static String getPropertyValue(String rootQualifier, Settings settings, int index) {
+    return settings.getString(getProperty(rootQualifier, index));
+  }
+
+  static private String getProperty(String rootQualifier, int index) {
+    if (index <= 3) {
+      return CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index;
+    } else {
+      return CoreProperties.TIMEMACHINE_PREFIX + "." + rootQualifier + "." + CoreProperties.TIMEMACHINE_PERIOD_SUFFIX + index;
     }
-    return conf.getString(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index, defaultValue);
   }
 
   public PastSnapshot findPreviousAnalysis(Snapshot projectSnapshot) {
index b4a9203073913aa4fbc4562960cec0e3b8236031..6d6903e007a68e6332ae22d96b653d65c7167946 100644 (file)
 package org.sonar.batch.components;
 
 import com.google.common.collect.Lists;
-import org.apache.commons.configuration.Configuration;
 import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchExtension;
+import org.sonar.api.config.Settings;
 import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.utils.Logs;
 
 import javax.persistence.Query;
 
@@ -37,29 +37,38 @@ import java.util.List;
 
 public class TimeMachineConfiguration implements BatchExtension {
 
+  private static final Logger LOG = LoggerFactory.getLogger(TimeMachineConfiguration.class);
+
   private static final int NUMBER_OF_VARIATION_SNAPSHOTS = 5;
   private static final int CORE_TENDENCY_DEPTH_DEFAULT_VALUE = 30;
 
   private Project project;
-  private final Configuration configuration;
+  private final Settings settings;
   private List<PastSnapshot> projectPastSnapshots;
   private DatabaseSession session;
 
-  public TimeMachineConfiguration(DatabaseSession session, Project project, Configuration configuration,
+  public TimeMachineConfiguration(DatabaseSession session, Project project, Settings settings,
       PastSnapshotFinder pastSnapshotFinder) {
     this.session = session;
     this.project = project;
-    this.configuration = configuration;
-    initPastSnapshots(pastSnapshotFinder);
+    this.settings = settings;
+    initPastSnapshots(pastSnapshotFinder, getRootProject(project).getQualifier());
+  }
+
+  private Project getRootProject(Project project){
+    if (!project.isRoot()) {
+     return getRootProject(project.getRoot());
+    }
+    return project;
   }
 
-  private void initPastSnapshots(PastSnapshotFinder pastSnapshotFinder) {
+  private void initPastSnapshots(PastSnapshotFinder pastSnapshotFinder, String rootQualifier) {
     Snapshot projectSnapshot = buildProjectSnapshot();
 
     projectPastSnapshots = Lists.newLinkedList();
     if (projectSnapshot != null) {
       for (int index = 1; index <= NUMBER_OF_VARIATION_SNAPSHOTS; index++) {
-        PastSnapshot pastSnapshot = pastSnapshotFinder.find(projectSnapshot, configuration, index);
+        PastSnapshot pastSnapshot = pastSnapshotFinder.find(projectSnapshot, rootQualifier, settings, index);
         if (pastSnapshot != null) {
           log(pastSnapshot);
           projectPastSnapshots.add(pastSnapshot);
@@ -91,9 +100,9 @@ public class TimeMachineConfiguration implements BatchExtension {
     String qualifier = pastSnapshot.getQualifier();
     // hack to avoid too many logs when the views plugin is installed
     if (StringUtils.equals(Qualifiers.VIEW, qualifier) || StringUtils.equals(Qualifiers.SUBVIEW, qualifier)) {
-      LoggerFactory.getLogger(getClass()).debug(pastSnapshot.toString());
+      LOG.debug(pastSnapshot.toString());
     } else {
-      Logs.INFO.info(pastSnapshot.toString());
+      LOG.info(pastSnapshot.toString());
     }
   }
 
@@ -106,6 +115,6 @@ public class TimeMachineConfiguration implements BatchExtension {
   }
 
   public boolean isFileVariationEnabled() {
-    return configuration.getBoolean("sonar.enableFileVariation", Boolean.FALSE);
+    return settings.getBoolean("sonar.enableFileVariation");
   }
 }
index ed37f5dcdbe84425856c5a66bb181bd5930ce669..1b4ba687767570cf57990979f4d66285cc35814b 100644 (file)
  */
 package org.sonar.batch.components;
 
-import org.apache.commons.configuration.BaseConfiguration;
-import org.apache.commons.configuration.Configuration;
-import org.apache.commons.configuration.PropertiesConfiguration;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
 import org.sonar.api.database.model.Snapshot;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
-import static org.mockito.Matchers.any;
 
 import static junit.framework.Assert.assertNull;
 import static org.hamcrest.Matchers.nullValue;
@@ -41,6 +38,7 @@ import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -69,12 +67,11 @@ public class PastSnapshotFinderTest {
 
   @Test
   public void shouldFind() {
-    Configuration conf = new BaseConfiguration();
-    conf.addProperty("sonar.timemachine.period5", "1.2");
+    Settings settings = new Settings().setProperty("sonar.timemachine.TRK.period5", "1.2");
 
     when(finderByVersion.findByVersion(null, "1.2")).thenReturn(new PastSnapshot("version", new Date(), new Snapshot()));
 
-    PastSnapshot variationSnapshot = finder.find(null, conf, 5);
+    PastSnapshot variationSnapshot = finder.find(null, "TRK", settings, 5);
 
     verify(finderByVersion).findByVersion(null, "1.2");
     assertThat(variationSnapshot.getIndex(), is(5));
@@ -199,27 +196,17 @@ public class PastSnapshotFinderTest {
 
   @Test
   public void shouldNotFailIfUnknownFormat() {
-    when(finderByPreviousAnalysis.findByPreviousAnalysis(null)).thenReturn(new PastSnapshot(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, new Date(), new Snapshot())); // should
-                                                                                                                                                                             // not
-                                                                                                                                                                             // be
-                                                                                                                                                                             // called
+    // should not be called
+    when(finderByPreviousAnalysis.findByPreviousAnalysis(null)).thenReturn(new PastSnapshot(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, new Date(), new Snapshot()));
+
     assertNull(finder.find(null, 2, "foooo"));
   }
 
   @Test
   public void shouldGetPropertyValue() {
-    PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty("sonar.timemachine.period1", "5");
-
-    assertThat(PastSnapshotFinder.getPropertyValue(conf, 1), is("5"));
-    assertThat(PastSnapshotFinder.getPropertyValue(conf, 999), nullValue());
-  }
-
-  @Test
-  public void shouldGetDefaultPropertyValue() {
-    PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty("sonar.timemachine.period1", "5");
+    Settings settings = new Settings().setProperty("sonar.timemachine.period1", "5");
 
-    assertThat(PastSnapshotFinder.getPropertyValue(conf, 2), is(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2));
+    assertThat(PastSnapshotFinder.getPropertyValue("FIL", settings, 1), is("5"));
+    assertThat(PastSnapshotFinder.getPropertyValue("FIL",settings, 999), nullValue());
   }
 }
index 1ba130b9e9cbc1d95a7e609b21a12a5404520103..33c15fb782ec743df0b8b9531b0484708f1b0ec9 100644 (file)
  */
 package org.sonar.batch.components;
 
-import org.apache.commons.configuration.PropertiesConfiguration;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
+import org.sonar.api.config.Settings;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.resources.Project;
 import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -34,29 +36,31 @@ import static org.mockito.Mockito.verifyZeroInteractions;
 
 public class TimeMachineConfigurationTest extends AbstractDbUnitTestCase {
 
-  @Test
-  public void should_init_past_snapshots() {
+  private Settings settings;
+  private PastSnapshotFinder pastSnapshotFinder;
+
+  @Before
+  public void before() {
     setupData("shared");
-    PropertiesConfiguration conf = new PropertiesConfiguration();
-    PastSnapshotFinder pastSnapshotFinder = mock(PastSnapshotFinder.class);
+    settings = new Settings();
+    pastSnapshotFinder = mock(PastSnapshotFinder.class);
+  }
 
-    new TimeMachineConfiguration(getSession(), new Project("my:project"), conf, pastSnapshotFinder);
+  @Test
+  public void should_init_past_snapshots() {
+    new TimeMachineConfiguration(getSession(), new Project("my:project"), settings, pastSnapshotFinder);
 
     verify(pastSnapshotFinder).find(argThat(new ArgumentMatcher<Snapshot>() {
       @Override
       public boolean matches(Object o) {
         return ((Snapshot) o).getResourceId() == 2 /* see database in shared.xml */;
       }
-    }), eq(conf), eq(1));
+    }), anyString(), eq(settings), eq(1));
   }
 
   @Test
   public void should_not_init_past_snapshots_if_first_analysis() {
-    setupData("shared");
-    PropertiesConfiguration conf = new PropertiesConfiguration();
-    PastSnapshotFinder pastSnapshotFinder = mock(PastSnapshotFinder.class);
-
-    new TimeMachineConfiguration(getSession(), new Project("new:project"), conf, pastSnapshotFinder);
+    new TimeMachineConfiguration(getSession(), new Project("new:project"), settings, pastSnapshotFinder);
 
     verifyZeroInteractions(pastSnapshotFinder);
   }
index e55d31b0f766486d00e7ac92a377b5d1557f0d68..7f9a14e051bdcab3a76c802053a39e6d43bc0f39 100644 (file)
@@ -306,7 +306,9 @@ public interface CoreProperties {
   String GOOGLE_ANALYTICS_ACCOUNT_PROPERTY = "sonar.google-analytics.account";
 
   /* Time machine periods */
-  String TIMEMACHINE_PERIOD_PREFIX = "sonar.timemachine.period";
+  String TIMEMACHINE_PREFIX = "sonar.timemachine";
+  String TIMEMACHINE_PERIOD_SUFFIX = "period";
+  String TIMEMACHINE_PERIOD_PREFIX = TIMEMACHINE_PREFIX + "." + TIMEMACHINE_PERIOD_SUFFIX;
   String TIMEMACHINE_MODE_PREVIOUS_ANALYSIS = "previous_analysis";
   String TIMEMACHINE_MODE_DATE = "date";
   String TIMEMACHINE_MODE_VERSION = "version";
index 319174e33056587ded808048464429f6f435be7c..ca57a78e8d0e49acc21c15536d980a6fbc4503c1 100644 (file)
@@ -54,7 +54,7 @@ import java.util.Map;
  */
 @Beta
 @Immutable
-public final class ResourceType {
+public class ResourceType {
 
   /**
    * Builder used to create {@link ResourceType} objects.
index 4f22a8e1d49269cbef85f4cb72336f6fb747f656..219dd603a2fe9654b39a027fb01e157a1ed52d36 100644 (file)
@@ -28,9 +28,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import org.sonar.api.BatchExtension;
 import org.sonar.api.ServerExtension;
-import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.task.TaskExtension;
 
 import javax.annotation.concurrent.Immutable;
@@ -44,7 +42,7 @@ import java.util.List;
  */
 @Beta
 @Immutable
-public final class ResourceTypeTree implements TaskExtension, ServerExtension {
+public class ResourceTypeTree implements TaskExtension, ServerExtension {
 
   private List<ResourceType> types;
   private ListMultimap<String, String> relations;
index 4e315e8d6ce43f8f5dd193101e9f5233f832511f..2cc67c37f7e7d9fded945d8559497c3b3f74ed7b 100644 (file)
@@ -44,7 +44,7 @@ import java.util.Map;
  * @since 2.14
  */
 @Beta
-public final class ResourceTypes implements TaskComponent, ServerComponent {
+public class ResourceTypes implements TaskComponent, ServerComponent {
 
   public static final Predicate<ResourceType> AVAILABLE_FOR_FILTERS = new Predicate<ResourceType>() {
     public boolean apply(@Nullable ResourceType input) {
@@ -175,4 +175,8 @@ public final class ResourceTypes implements TaskComponent, ServerComponent {
     return treeByQualifier.get(qualifier);
   }
 
+  public ResourceType getRoot(String qualifier){
+    return getTree(qualifier).getRootType();
+  }
+
 }
index 440bd1fd60bc45d75bccacda7bd50a2224bfd799..b2dddeb572fd4f2465df534ec62df86cf0d68998 100644 (file)
@@ -55,35 +55,35 @@ public class ResourceTypesTest {
   }
 
   @Test
-  public void getAll() {
+  public void get_all() {
     assertThat(qualifiers(types.getAll())).containsOnly(Qualifiers.PROJECT, Qualifiers.DIRECTORY, Qualifiers.FILE, Qualifiers.VIEW, Qualifiers.SUBVIEW);
   }
 
   @Test
-  public void getRoots() {
+  public void get_roots() {
     assertThat(qualifiers(types.getRoots())).containsOnly(Qualifiers.PROJECT, Qualifiers.VIEW);
   }
 
   @Test
-  public void getAll_predicate() {
+  public void get_all_predicate() {
     Collection<ResourceType> forFilters = types.getAll(ResourceTypes.AVAILABLE_FOR_FILTERS);
     assertThat(qualifiers(forFilters)).containsOnly(Qualifiers.PROJECT, Qualifiers.VIEW).doesNotHaveDuplicates();
   }
 
   @Test
-  public void getAllWithPropertyKey() {
+  public void get_all_with_property_key() {
     assertThat(qualifiers(types.getAllWithPropertyKey("supportsMeasureFilters"))).containsOnly(Qualifiers.VIEW, Qualifiers.PROJECT);
   }
 
   @Test
-  public void getAllWithPropertyValue() {
+  public void get_all_with_property_value() {
     assertThat(qualifiers(types.getAllWithPropertyValue("supportsMeasureFilters", "true"))).containsOnly(Qualifiers.VIEW, Qualifiers.PROJECT);
     assertThat(qualifiers(types.getAllWithPropertyValue("supportsMeasureFilters", true))).containsOnly(Qualifiers.VIEW, Qualifiers.PROJECT);
     assertThat(qualifiers(types.getAllWithPropertyValue("supportsMeasureFilters", false))).containsOnly(Qualifiers.SUBVIEW, Qualifiers.DIRECTORY, Qualifiers.FILE);
   }
 
   @Test
-  public void getChildrenQualifiers() {
+  public void get_children_qualifiers() {
     assertThat(types.getChildrenQualifiers(Qualifiers.PROJECT)).containsExactly(Qualifiers.DIRECTORY);
     assertThat(types.getChildrenQualifiers(Qualifiers.SUBVIEW)).containsExactly(Qualifiers.PROJECT);
     assertThat(types.getChildrenQualifiers("xxx")).isEmpty();
@@ -91,13 +91,13 @@ public class ResourceTypesTest {
   }
 
   @Test
-  public void getChildren() {
+  public void get_children() {
     assertThat(qualifiers(types.getChildren(Qualifiers.PROJECT))).contains(Qualifiers.DIRECTORY);
     assertThat(qualifiers(types.getChildren(Qualifiers.SUBVIEW))).contains(Qualifiers.PROJECT);
   }
 
   @Test
-  public void getLeavesQualifiers() {
+  public void get_leaves_qualifiers() {
     assertThat(types.getLeavesQualifiers(Qualifiers.PROJECT)).containsExactly(Qualifiers.FILE);
 
     assertThat(types.getLeavesQualifiers(Qualifiers.DIRECTORY)).containsExactly(Qualifiers.FILE);
@@ -108,13 +108,18 @@ public class ResourceTypesTest {
   }
 
   @Test
-  public void getTree() {
+  public void get_tree() {
     assertThat(qualifiers(types.getTree(Qualifiers.VIEW).getTypes())).containsOnly(Qualifiers.VIEW, Qualifiers.SUBVIEW).doesNotHaveDuplicates();
     assertThat(types.getTree("xxx")).isNull();
   }
 
+  @Test
+  public void get_root() {
+    assertThat(types.getRoot(Qualifiers.FILE).getQualifier()).isEqualTo(Qualifiers.PROJECT);
+  }
+
   @Test(expected = IllegalStateException.class)
-  public void failOnDuplicatedQualifier() {
+  public void fail_on_duplicated_qualifier() {
     ResourceTypeTree tree1 = ResourceTypeTree.builder()
         .addType(ResourceType.builder("foo").build())
         .build();