Skip to content

Commit e7512dd

Browse files
PLUGINAPI-190 Promote hidden to a first-class flag on PropertyDefinition (#290)
Hidden can now be used with any scope, and carry the visibility of the property independently. property configuration through annotation is conserved, but not updated to support usage of hidden directly.
1 parent 8e22502 commit e7512dd

2 files changed

Lines changed: 68 additions & 22 deletions

File tree

plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ private String getKey() {
136136
private final String category;
137137
private final List<ConfigScope> configScopes;
138138
private final boolean global;
139+
private final boolean hidden;
139140
private final boolean multiValues;
140141
private final String deprecatedKey;
141142
private final List<PropertyFieldDefinition> fields;
@@ -153,6 +154,7 @@ private PropertyDefinition(Builder builder) {
153154
this.category = builder.category;
154155
this.subCategory = builder.subCategory;
155156
this.global = builder.global;
157+
this.hidden = builder.hidden;
156158
this.type = builder.type;
157159
this.options = builder.options;
158160
this.multiValues = builder.multiValues;
@@ -190,9 +192,19 @@ static PropertyDefinition create(Property annotation) {
190192
configScopes.add(ConfigScope.MODULE);
191193
}
192194
if (annotation.global()) {
193-
builder.onConfigScopes(configScopes);
195+
if (!configScopes.isEmpty()) {
196+
// visible and settable at instance and scope level
197+
builder.onConfigScopes(configScopes);
198+
}
199+
// else default state, visible and settable at instance level only
194200
} else {
195-
builder.onlyOnConfigScopes(configScopes);
201+
if (!configScopes.isEmpty()) {
202+
// visible and settable at scope only
203+
builder.onlyOnConfigScopes(configScopes);
204+
} else {
205+
// legacy pattern for not visible but settable at instance level only
206+
builder.hidden();
207+
}
196208
}
197209
return builder.build();
198210
}
@@ -344,6 +356,14 @@ public boolean global() {
344356
return global;
345357
}
346358

359+
/**
360+
* Hidden properties are not discoverable, and typically don't appear in the UI.
361+
* They can only be configured via the API.
362+
*/
363+
public boolean hidden() {
364+
return hidden;
365+
}
366+
347367
public boolean multiValues() {
348368
return multiValues;
349369
}
@@ -667,11 +687,7 @@ public PropertyDefinition build() {
667687
checkArgument(!isEmpty(key), "Key must be set");
668688
fixType(key, type);
669689
checkArgument(onConfigScopes.isEmpty() || onlyOnConfigScopes.isEmpty(), "Cannot use both forQualifiers and onlyForQualifiers");
670-
checkArgument(!hidden || (onConfigScopes.isEmpty() && onlyOnConfigScopes.isEmpty()), "Cannot be hidden and defining qualifiers on which to display");
671690
checkArgument(!JSON.equals(type) || !multiValues, "Multivalues are not allowed to be defined for JSON-type property.");
672-
if (hidden) {
673-
global = false;
674-
}
675691
if (!fields.isEmpty()) {
676692
type = PROPERTY_SET;
677693
}

plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,19 @@ public void should_create_from_annotation() {
9696
assertThat(def.configScopes()).containsOnly(ConfigScope.PROJECT, ConfigScope.MODULE);
9797
assertThat(def.multiValues()).isTrue();
9898
assertThat(def.fields()).isEmpty();
99+
assertThat(def.hidden()).isFalse();
100+
}
101+
102+
@Test
103+
public void should_translate_legacy_global_false_idiom_to_hidden() {
104+
Properties props = AnnotationUtils.getAnnotation(LegacyHiddenIdiom.class, Properties.class);
105+
Property prop = props.value()[0];
106+
107+
PropertyDefinition def = PropertyDefinition.create(prop);
108+
109+
assertThat(def.hidden()).isTrue();
110+
assertThat(def.global()).isTrue();
111+
assertThat(def.configScopes()).isEmpty();
99112
}
100113

101114
@Test
@@ -108,7 +121,34 @@ public void should_create_hidden_property() {
108121
assertThat(def.key()).isEqualTo("hello");
109122
assertThat(def.qualifiers()).isEmpty();
110123
assertThat(def.configScopes()).isEmpty();
124+
assertThat(def.global()).isTrue();
125+
assertThat(def.hidden()).isTrue();
126+
}
127+
128+
@Test
129+
public void should_create_hidden_property_with_on_config_scopes() {
130+
PropertyDefinition def = PropertyDefinition.builder("hello")
131+
.name("Hello")
132+
.hidden()
133+
.onConfigScopes(ConfigScope.PROJECT)
134+
.build();
135+
136+
assertThat(def.hidden()).isTrue();
137+
assertThat(def.global()).isTrue();
138+
assertThat(def.configScopes()).containsExactly(ConfigScope.PROJECT);
139+
}
140+
141+
@Test
142+
public void should_create_hidden_property_with_only_on_config_scopes() {
143+
PropertyDefinition def = PropertyDefinition.builder("hello")
144+
.name("Hello")
145+
.hidden()
146+
.onlyOnConfigScopes(ConfigScope.PROJECT)
147+
.build();
148+
149+
assertThat(def.hidden()).isTrue();
111150
assertThat(def.global()).isFalse();
151+
assertThat(def.configScopes()).containsExactly(ConfigScope.PROJECT);
112152
}
113153

114154
@Test
@@ -125,6 +165,7 @@ public void should_create_property_with_default_values() {
125165
assertThat(def.description()).isEmpty();
126166
assertThat(def.type()).isEqualTo(PropertyType.STRING);
127167
assertThat(def.global()).isTrue();
168+
assertThat(def.hidden()).isFalse();
128169
assertThat(def.qualifiers()).isEmpty();
129170
assertThat(def.configScopes()).isEmpty();
130171
assertThat(def.multiValues()).isFalse();
@@ -150,6 +191,7 @@ public void should_create_from_annotation_default_values() {
150191
assertThat(def.configScopes()).isEmpty();
151192
assertThat(def.multiValues()).isFalse();
152193
assertThat(def.fields()).isEmpty();
194+
assertThat(def.hidden()).isFalse();
153195
}
154196

155197
@Test
@@ -320,22 +362,6 @@ public void should_not_create_json_multivalue() {
320362
.hasMessage("Multivalues are not allowed to be defined for JSON-type property.");
321363
}
322364

323-
@Test
324-
public void should_not_authorize_defining_on_qualifiers_and_hidden() {
325-
Builder builder = PropertyDefinition.builder("foo").name("foo").onQualifiers(org.sonar.api.resources.Qualifiers.PROJECT).hidden();
326-
assertThatThrownBy(builder::build)
327-
.isInstanceOf(IllegalArgumentException.class)
328-
.hasMessage("Cannot be hidden and defining qualifiers on which to display");
329-
}
330-
331-
@Test
332-
public void should_not_authorize_defining_ony_on_qualifiers_and_hidden() {
333-
Builder builder = PropertyDefinition.builder("foo").name("foo").onlyOnQualifiers(org.sonar.api.resources.Qualifiers.PROJECT).hidden();
334-
assertThatThrownBy(builder::build)
335-
.isInstanceOf(IllegalArgumentException.class)
336-
.hasMessage("Cannot be hidden and defining qualifiers on which to display");
337-
}
338-
339365
@Test
340366
public void should_not_authorize_defining_on_qualifiers_and_only_on_qualifiers() {
341367
Builder builder = PropertyDefinition.builder("foo").name("foo").onQualifiers(org.sonar.api.resources.Qualifiers.MODULE)
@@ -470,4 +496,8 @@ static class WithPropertySet {
470496
static class DefaultValues {
471497
}
472498

499+
@Properties(@Property(key = "hello", name = "Hello", global = false))
500+
static class LegacyHiddenIdiom {
501+
}
502+
473503
}

0 commit comments

Comments
 (0)