SLING-9952 : Skip validation of properties if they contain a placeholder
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
index f07df67..584db9c 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescription.java
@@ -220,10 +220,18 @@
/**
* Set the cardinality
- * @param cardinality the cardinality to set
+ * The default cardinality is {@code 1}. If the value is greater than zero
+ * the property can contain up to that number of values.
+ * If the cardinality is {@code -1} the property can hold an unlimited number
+ * of values.
+ * @param value the cardinality to set
+ * @throws IllegalArgumentException If the value is {@code 0} or below {@code -1}.
*/
- public void setCardinality(final int cardinality) {
- this.cardinality = cardinality;
+ public void setCardinality(final int value) {
+ if ( value == 0 || value < -1 ) {
+ throw new IllegalArgumentException();
+ }
+ this.cardinality = value;
}
/**
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
index a9d5554..ecfdb5a 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidator.java
@@ -26,6 +26,7 @@
import org.apache.sling.feature.extension.apiregions.api.config.Option;
import org.apache.sling.feature.extension.apiregions.api.config.PropertyDescription;
+import org.apache.sling.feature.extension.apiregions.api.config.PropertyType;
/**
* Validate a configuration property or framework property
@@ -122,61 +123,80 @@
private static final List<String> PLACEHOLDERS = Arrays.asList("$[env:", "$[secret:", "$[prop:");
- void validateValue(final PropertyDescription prop, final Object value, final PropertyValidationResult result) {
+ void validateValue(final PropertyDescription desc, final Object value, final PropertyValidationResult result) {
final List<String> messages = result.getErrors();
if ( value != null ) {
// check for placeholder
- boolean checkValue = true;
+ boolean hasPlaceholder = false;
if ( value instanceof String ) {
final String strVal = (String)value;
for(final String p : PLACEHOLDERS) {
if ( strVal.contains(p) ) {
- checkValue = false;
+ hasPlaceholder = true;
break;
}
}
}
- if ( checkValue ) {
- switch ( prop.getType() ) {
- case BOOLEAN : validateBoolean(prop, value, messages);
+ if ( !hasPlaceholder ) {
+ switch ( desc.getType() ) {
+ case BOOLEAN : validateBoolean(desc, value, messages);
break;
- case BYTE : validateByte(prop, value, messages);
+ case BYTE : validateByte(desc, value, messages);
break;
- case CHARACTER : validateCharacter(prop, value, messages);
+ case CHARACTER : validateCharacter(desc, value, messages);
break;
- case DOUBLE : validateDouble(prop, value, messages);
+ case DOUBLE : validateDouble(desc, value, messages);
break;
- case FLOAT : validateFloat(prop, value, messages);
+ case FLOAT : validateFloat(desc, value, messages);
break;
- case INTEGER : validateInteger(prop, value, messages);
+ case INTEGER : validateInteger(desc, value, messages);
break;
- case LONG : validateLong(prop, value, messages);
+ case LONG : validateLong(desc, value, messages);
break;
- case SHORT : validateShort(prop, value, messages);
+ case SHORT : validateShort(desc, value, messages);
break;
- case STRING : // no special validation for string
+ case STRING : validateRequired(desc, value, messages);
break;
- case EMAIL : validateEmail(prop, value, messages);
+ case EMAIL : validateEmail(desc, value, messages);
break;
- case PASSWORD : validatePassword(prop, value, messages);
+ case PASSWORD : validatePassword(desc, value, messages, false);
break;
- case URL : validateURL(prop, value, messages);
+ case URL : validateURL(desc, value, messages);
break;
- case PATH : validatePath(prop, value, messages);
+ case PATH : validatePath(desc, value, messages);
break;
- default : messages.add("Unable to validate value - unknown property type : " + prop.getType());
+ default : messages.add("Unable to validate value - unknown property type : " + desc.getType());
}
- validateRegex(prop, value, messages);
- validateOptions(prop, value, messages);
+ validateRegex(desc, value, messages);
+ validateOptions(desc, value, messages);
} else {
- result.markSkipped();
+ // placeholder is present
+ if ( desc.getType() == PropertyType.PASSWORD ) {
+ validatePassword(desc, value, messages, true);
+ } else if ( desc.getType() == PropertyType.STRING ) {
+ // any string is valid, we only mark the result as skipped if a regex or options are set
+ if ( desc.getRegex() != null || desc.getOptions() != null || desc.isRequired() ) {
+ result.markSkipped();
+ }
+ } else {
+ result.markSkipped();
+ }
}
} else {
messages.add("Null value provided for validation");
}
}
- void validateBoolean(final PropertyDescription prop, final Object value, final List<String> messages) {
+ void validateRequired(final PropertyDescription prop, final Object value, final List<String> messages) {
+ if ( prop.isRequired() ) {
+ final String val = value.toString();
+ if ( val.isEmpty() ) {
+ messages.add("Value is required");
+ }
+ }
+ }
+
+ void validateBoolean(final PropertyDescription prop, final Object value, final List<String> messages) {
if ( ! (value instanceof Boolean) ) {
if ( value instanceof String ) {
final String v = (String)value;
@@ -333,10 +353,10 @@
}
}
- void validatePassword(final PropertyDescription prop, final Object value, final List<String> messages) {
- if ( prop.getVariable() == null ) {
- messages.add("Value for a password must use a variable");
- }
+ void validatePassword(final PropertyDescription desc, final Object value, final List<String> messages, final boolean hasPlaceholder) {
+ if ( !hasPlaceholder ) {
+ messages.add("Value for a password must use a placeholder");
+ }
}
void validatePath(final PropertyDescription prop, final Object value, final List<String> messages) {
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
index d9b50c2..5e64e62 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.Arrays;
@@ -133,4 +134,27 @@
assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
}
+
+ @Test public void testSetCardinality() {
+ final PropertyDescription desc = new PropertyDescription();
+ desc.setCardinality(5);
+ assertEquals(5, desc.getCardinality());
+ desc.setCardinality(1);
+ assertEquals(1, desc.getCardinality());
+ desc.setCardinality(-1);
+ assertEquals(-1, desc.getCardinality());
+
+ try {
+ desc.setCardinality(0);
+ fail();
+ } catch ( final IllegalArgumentException iae) {
+ // expected
+ }
+ try {
+ desc.setCardinality(-2);
+ fail();
+ } catch ( final IllegalArgumentException iae) {
+ // expected
+ }
+ }
}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
index fd8d1c0..dd544bd 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/validation/PropertyValidatorTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.apache.sling.feature.extension.apiregions.api.config.Option;
@@ -278,8 +279,10 @@
assertTrue(result.isValid());
assertFalse(result.isSkipped());
- prop.setVariable("secret");
- result = validator.validate(null, prop);
+ result = validator.validate("secret", prop);
+ assertFalse(result.isValid());
+
+ result = validator.validate("$[secret:dbpassword]", prop);
assertTrue(result.isValid());
assertFalse(result.isSkipped());
}
@@ -299,6 +302,52 @@
assertFalse(result.isSkipped());
}
+ @Test public void testValidateString() {
+ final PropertyDescription desc = new PropertyDescription();
+ PropertyValidationResult result;
+
+ result = validator.validate("hello world", desc);
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+
+ result = validator.validate("$[prop:KEY]", desc);
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+
+ // skip if required
+ desc.setRequired(true);
+ result = validator.validate("$[prop:KEY]", desc);
+ assertTrue(result.isValid());
+ assertTrue(result.isSkipped());
+ desc.setRequired(false);
+
+ // skip if options
+ desc.setOptions(Collections.singletonList(new Option()));
+ result = validator.validate("$[prop:KEY]", desc);
+ assertTrue(result.isValid());
+ assertTrue(result.isSkipped());
+ desc.setOptions(null);
+
+ // skip if regexp
+ desc.setRegex(".*");
+ result = validator.validate("$[prop:KEY]", desc);
+ assertTrue(result.isValid());
+ assertTrue(result.isSkipped());
+ desc.setRegex(null);
+
+ // empty string - not required
+ result = validator.validate("", desc);
+ assertTrue(result.isValid());
+ assertFalse(result.isSkipped());
+
+ // empty string - required
+ desc.setRequired(true);
+ result = validator.validate("", desc);
+ assertFalse(result.isValid());
+ assertFalse(result.isSkipped());
+ desc.setRequired(false);
+ }
+
@Test public void testValidateRange() {
final List<String> messages = new ArrayList<>();
final PropertyDescription prop = new PropertyDescription();
@@ -510,6 +559,7 @@
@Test public void testPlaceholdersString() {
final PropertyDescription desc = new PropertyDescription();
+ desc.setType(PropertyType.PATH);
PropertyValidationResult result = null;