[java-metadata-aggregator] 03/03: MDA-233 - Better value context indication from validators
Ian Young
ian at iay.org.uk
Tue Mar 26 11:20:18 UTC 2024
This is an automated email from the git hooks/post-receive script.
iay pushed a commit to branch main
in repository java-metadata-aggregator.
View the commit online:
http://git.shibboleth.net/view/?p=java-metadata-aggregator.git;a=commit;h=b8991e4d215f42b03289c5ba0d112f7cfb708dae
commit b8991e4d215f42b03289c5ba0d112f7cfb708dae
Author: Ian Young <ian at iay.org.uk>
AuthorDate: Tue Mar 26 11:19:45 2024 +0000
MDA-233 - Better value context indication from validators
- Add a second validate method with a valueContext parameter
- Both validate methods now have default method bodies;
implementing classes can provide either or both.
- Re-worked various classes to pass this down through a stack
of validations.
- BaseAsValidator now interpolates a valueContext before calling
sub-validators.
- Implemented extended message handling (in a non-templated way,
for now) for the URL validator classes, which need this most.
https://shibboleth.atlassian.net/browse/MDA-233
---
.../metadata/validate/BaseAsValidator.java | 13 +++-
.../metadata/validate/BaseValidator.java | 80 ++++++++++++++++++++--
.../shibboleth/metadata/validate/Validator.java | 65 +++++++++++++++++-
.../metadata/validate/ValidatorSequence.java | 7 +-
.../validate/url/EmptyPortURLValidator.java | 20 +++++-
.../validate/url/HTTPSProtocolURLValidator.java | 13 +++-
.../validate/url/MissingHostURLValidator.java | 18 ++++-
.../dom/StringAttributeValidationStageTest.java | 18 ++---
.../net/shibboleth/metadata/testing/BaseTest.java | 17 +++++
.../metadata/validate/BaseAsValidatorTest.java | 46 ++++++-------
.../metadata/validate/BaseValidatorTest.java | 3 +
.../metadata/validate/ValidatorSequenceTest.java | 18 +++++
.../validate/string/AsURLStringValidatorTest.java | 67 ++++++++++++++++--
.../metadata/validate/testing/BoomAsValidator.java | 35 ++++++++++
...ctingValidator.java => CapturingValidator.java} | 37 ++++++----
.../validate/testing/PassThroughAsValidator.java | 33 +++++++++
.../validate/url/EmptyPortURLValidatorTest.java | 27 ++++++--
.../url/HTTPSProtocolURLValidatorTest.java | 24 ++++++-
.../validate/url/MissingHostURLValidatorTest.java | 41 +++++++++++
19 files changed, 505 insertions(+), 77 deletions(-)
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseAsValidator.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseAsValidator.java
index 7349366..6a430bf 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseAsValidator.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseAsValidator.java
@@ -17,6 +17,7 @@ package net.shibboleth.metadata.validate;
import java.util.List;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
@@ -108,11 +109,17 @@ public abstract class BaseAsValidator<V, A> extends BaseValidator implements Val
protected abstract @Nonnull A convert(@Nonnull final V from) throws IllegalArgumentException;
@Override
- public @Nonnull Action validate(@Nonnull final V t, @Nonnull final Item<?> item, @Nonnull final String callerId)
- throws StageProcessingException {
+ public @Nonnull Action validate(final @Nonnull V t, final @Nonnull Item<?> item, final @Nonnull String callerId,
+ final @Nullable String valueContext) throws StageProcessingException {
try {
final A v = convert(t);
- return validators.validate(v, item, makeComponentId(callerId));
+
+ // If called without an explicit value context, interpose the
+ // string form of the value we have just converted. In many cases,
+ // that will have been a String anyway.
+ final String context = valueContext == null ? t.toString() : valueContext;
+
+ return validators.validate(v, item, makeComponentId(callerId), context);
} catch (final IllegalArgumentException e) {
if (isConversionRequired()) {
addErrorMessage(t, item, callerId, e);
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseValidator.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseValidator.java
index 4166816..7021028 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseValidator.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/BaseValidator.java
@@ -28,8 +28,10 @@ import net.shibboleth.shared.logic.Constraint;
/**
* Base class for validator implementations.
*
+ * <p>
* Encapsulates the notion of an identifier for each validator class, and helper
* methods for constructing status metadata.
+ * </p>
*
* @since 0.9.0
*/
@@ -39,16 +41,41 @@ public abstract class BaseValidator extends AbstractIdentifiableInitializableCom
/**
* Message format string.
*
- * The generated message is formatted using this with the object being validated passed
+ * <p>
+ * The generated message is formatted using this with the value being validated passed
* as an argument.
- *
- * Defaults to <code>"value rejected: '%s'"</code>.
+ * <p>
+ *
+ * <p>
+ * Defaults to <code>"value rejected: '%s'"</code>. May be overwritten by a subclass via
+ * its constructor, or by setting the {@code message} property explicitly as configuration.
+ * </p>
*
* @since 0.10.0
*/
@Nonnull @GuardedBy("this")
- private String message = "value rejected: '%s'";
+ private String message;
+ /*
+ * Constructor.
+ *
+ * @since 0.10.0
+ */
+ protected BaseValidator() {
+ this.message = "value rejected: '%s'";
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param message default value for the {@code message} property
+ *
+ * @since 0.10.0
+ */
+ protected BaseValidator(final @Nonnull String message) {
+ this.message = message;
+ }
+
/**
* Returns the message format string.
*
@@ -126,6 +153,26 @@ public abstract class BaseValidator extends AbstractIdentifiableInitializableCom
}
}
+ /**
+ * Formats a value context for presentation as a message prefix.
+ *
+ * <p>
+ * If there is no explicit value context, returns an empty string.
+ * </p>
+ *
+ * @param valueContext value context string, or {@code null}
+ * @return formatted value context prefix
+ *
+ * @since 0.10.0
+ */
+ private @Nonnull String formatValueContext(final @Nullable String valueContext) {
+ if (valueContext == null) {
+ return "";
+ } else {
+ return "'" + valueContext + "': ";
+ }
+ }
+
/**
* Formats the message with the given subject.
*
@@ -166,6 +213,31 @@ public abstract class BaseValidator extends AbstractIdentifiableInitializableCom
}
}
+ /**
+ * Add an {@link ErrorStatus} to the given {@link Item}.
+ *
+ * <p>
+ * The status message included in the {@link ErrorStatus} is generated
+ * by formatting the provided value with the {@link #message} field.
+ * </p>
+ *
+ * <p>
+ * If a {@code valueContext} has been provided, it will be included
+ * in the resulting message as a prefix.
+ * </p>
+ *
+ * @param extra extra value to include in the status metadata
+ * @param item {@link Item} to add the status metadata to
+ * @param callerId a {@link String} identifying the caller
+ * @param valueContext the context of the validation, or {@code null}
+ *
+ * @since 0.10.0
+ */
+ protected void addErrorMessage(@Nonnull final Object extra, @Nonnull final Item<?> item,
+ @Nonnull final String callerId, @Nullable final String valueContext) {
+ addError(formatValueContext(valueContext) + formatMessage(extra), item, callerId);
+ }
+
/**
* Add an {@link ErrorStatus} to the given {@link Item}.
*
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/Validator.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/Validator.java
index f4dddf4..cc7a332 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/Validator.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/Validator.java
@@ -15,6 +15,7 @@
package net.shibboleth.metadata.validate;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import net.shibboleth.metadata.Item;
@@ -24,7 +25,21 @@ import net.shibboleth.shared.component.IdentifiableComponent;
import net.shibboleth.shared.component.InitializableComponent;
/**
- * Interface for a validator to be applied to an value in the context of a given {@link Item}.
+ * Interface for a validator to be applied to an value in the context of a given {@link Item},
+ * to which status metadata will be added when appropriate.
+ *
+ * <p>
+ * A {@code validate} call may be provided with a "value context" {@link String} indicating
+ * the larger validation of which this call is a component. For example, a validation of
+ * the host component of a URL value might be provided with a value context of the
+ * entire URL. If the value context is not required, it may be omitted either by passing {@code null}
+ * or by using the {@code validate} method lacking that parameter.
+ * </p>
+ *
+ * <p>
+ * The interface provides default method bodies for both forms of {@code validate} so
+ * that implementing classes only need to provide one or the other in most cases.
+ * </p>
*
* <p>
* {@code Validator}s <strong>must</strong> be thread-safe.
@@ -72,13 +87,57 @@ public interface Validator<V> extends DestructableComponent, IdentifiableCompone
* normally use a {@code componentId} of <code>stage/val</code>.
* </p>
*
+ * <p>
+ * The <code>valueContext</code> makes the original context of the <code>value</code>
+ * available to help in constructing meaningful error messages. For example, the
+ * validation of a complex structured value such as a URI might be broken down into
+ * multiple sub-validations of its components: scheme, for example. When relevant, the
+ * <code>valueContext</code> provides a representation of the original complex value
+ * while </code>value</code> is the component currently being validated.
+ * </p>
+ *
* @param value the value to be validated
* @param item the {@link Item} context for the validation
* @param callerId a {@link String} identifying the caller
+ * @param valueContext an additional {@link String} indicating the value context, or {@code null}
* @return an indication of whether to process additional validators
* @throws StageProcessingException if an error occurs during validation
+ *
+ * @since 0.10.0
*/
+ default
+ @Nonnull Action validate(@Nonnull V value, @Nonnull Item<?> item, @Nonnull String callerId, @Nullable String valueContext)
+ throws StageProcessingException {
+ return validate(value, item, callerId);
+ }
+
+ /**
+ * Apply the validator to a value in the context of the given {@link Item}.
+ *
+ * <p>
+ * The validator influences future processing by adding item metadata to the {@link Item}.
+ * </p>
+ *
+ * <p>
+ * A common case is that the validator will add a {@link net.shibboleth.metadata.StatusMetadata}
+ * to the {@link Item}, for example a {@link net.shibboleth.metadata.ErrorStatus}.
+ * In this case, the convention is that the {@code componentId} of the
+ * {@link net.shibboleth.metadata.ErrorStatus} would be created by combining the
+ * {@code callerId} with a <code>/</code> and the validator's own identifier.
+ * For example, a validator {@code val} called by a stage {@code stage} would
+ * normally use a {@code componentId} of <code>stage/val</code>.
+ * </p>
+ *
+ * @param value the value to be validated
+ * @param item the {@link Item} context for the validation
+ * @param callerId a {@link String} identifying the caller
+ * @return an indication of whether to process additional validators
+ * @throws StageProcessingException if an error occurs during validation
+ */
+ default
@Nonnull Action validate(@Nonnull V value, @Nonnull Item<?> item, @Nonnull String callerId)
- throws StageProcessingException;
-
+ throws StageProcessingException {
+ return validate(value, item, callerId, null);
+ }
+
}
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/ValidatorSequence.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/ValidatorSequence.java
index a5f681e..846e82c 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/ValidatorSequence.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/ValidatorSequence.java
@@ -17,6 +17,7 @@ package net.shibboleth.metadata.validate;
import java.util.List;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
@@ -68,10 +69,10 @@ public class ValidatorSequence<V> extends BaseValidator implements Validator<V>
}
@Override
- public @Nonnull Action validate(@Nonnull final V value, @Nonnull final Item<?> item, @Nonnull final String callerId)
- throws StageProcessingException {
+ public @Nonnull Action validate(@Nonnull final V value, @Nonnull final Item<?> item, @Nonnull final String callerId,
+ @Nullable final String valueContext) throws StageProcessingException {
for (final Validator<V> validator: getValidators()) {
- final Action action = validator.validate(value, item, callerId);
+ final Action action = validator.validate(value, item, callerId, valueContext);
if (action == Action.DONE) {
return action;
}
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidator.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidator.java
index 88a1f0c..58c54ee 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidator.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidator.java
@@ -17,6 +17,7 @@ package net.shibboleth.metadata.validate.url;
import java.net.URL;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.shibboleth.metadata.Item;
import net.shibboleth.metadata.pipeline.StageProcessingException;
@@ -25,16 +26,31 @@ import net.shibboleth.metadata.validate.Validator;
/**
* Validates that a {@link URL} does not contain an empty port component.
+ *
+ * @since 0.10.0
*/
public class EmptyPortURLValidator extends BaseValidator implements Validator<URL> {
+ /**
+ * Constructor.
+ *
+ * <p>
+ * Note: as this validator checks for an empty value, there is no benefit to
+ * including that value in the formatted message, so the placeholder is not included in
+ * the formatting string.
+ * </p>
+ */
+ public EmptyPortURLValidator() {
+ super("libxml2: port present but empty");
+ }
+
@Override
public @Nonnull Action validate(final @Nonnull URL url, final @Nonnull Item<?> item,
- final @Nonnull String callerId) throws StageProcessingException {
+ final @Nonnull String callerId, final @Nullable String valueContext) throws StageProcessingException {
final String authority = url.getAuthority();
if (authority != null && !authority.isEmpty()) {
if (authority.charAt(authority.length() - 1) == ':') {
- addError("libxml2: port present but empty", item, callerId);
+ addErrorMessage(authority, item, callerId, valueContext);
return Action.DONE;
}
}
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidator.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidator.java
index 6e83204..859904d 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidator.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidator.java
@@ -17,6 +17,7 @@ package net.shibboleth.metadata.validate.url;
import java.net.URL;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.shibboleth.metadata.Item;
import net.shibboleth.metadata.pipeline.StageProcessingException;
@@ -29,12 +30,20 @@ import net.shibboleth.metadata.validate.Validator;
*/
public class HTTPSProtocolURLValidator extends BaseValidator implements Validator<URL> {
+ /**
+ * Constructor.
+ */
+ public HTTPSProtocolURLValidator() {
+ super("protocol '%s' must be https");
+ }
+
@Override
public @Nonnull Action validate(final @Nonnull URL url, final @Nonnull Item<?> item,
- final @Nonnull String callerId) throws StageProcessingException {
+ final @Nonnull String callerId, final @Nullable String valueContext) throws StageProcessingException {
final var protocol = url.getProtocol();
+ assert protocol != null;
if (!"https".equals(protocol)) {
- addError("protocol '" + protocol + "' must be https", item, callerId);
+ addErrorMessage(protocol, item, callerId, valueContext);
return Action.DONE;
}
return Action.CONTINUE;
diff --git a/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/MissingHostURLValidator.java b/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/MissingHostURLValidator.java
index d082d6c..882199b 100644
--- a/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/MissingHostURLValidator.java
+++ b/mda-framework/src/main/java/net/shibboleth/metadata/validate/url/MissingHostURLValidator.java
@@ -17,6 +17,7 @@ package net.shibboleth.metadata.validate.url;
import java.net.URL;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.shibboleth.metadata.Item;
import net.shibboleth.metadata.pipeline.StageProcessingException;
@@ -28,11 +29,24 @@ import net.shibboleth.metadata.validate.Validator;
*/
public class MissingHostURLValidator extends BaseValidator implements Validator<URL> {
+ /**
+ * Constructor.
+ *
+ * <p>
+ * Note: as this validator checks for an empty value, there is no benefit to
+ * including that value in the formatted message, so the placeholder is not included in
+ * the formatting string.
+ * </p>
+ */
+ public MissingHostURLValidator() {
+ super("host name not present");
+ }
+
@Override
public @Nonnull Action validate(final @Nonnull URL url, final @Nonnull Item<?> item,
- final @Nonnull String callerId) throws StageProcessingException {
+ final @Nonnull String callerId, final @Nullable String valueContext) throws StageProcessingException {
if ("".equals(url.getHost())) {
- addError("host name not present", item, callerId);
+ addErrorMessage("", item, callerId, valueContext);
return Action.DONE;
}
return Action.CONTINUE;
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/dom/StringAttributeValidationStageTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/dom/StringAttributeValidationStageTest.java
index 60c5089..3b669a1 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/dom/StringAttributeValidationStageTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/dom/StringAttributeValidationStageTest.java
@@ -16,7 +16,7 @@ import net.shibboleth.metadata.dom.saml.SAMLMetadataSupport;
import net.shibboleth.metadata.dom.testing.BaseDOMTest;
import net.shibboleth.metadata.validate.RejectAllValidator;
import net.shibboleth.metadata.validate.Validator;
-import net.shibboleth.metadata.validate.testing.CollectingValidator;
+import net.shibboleth.metadata.validate.testing.CapturingValidator;
import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.xml.XMLConstants;
@@ -80,7 +80,7 @@ public class StringAttributeValidationStageTest extends BaseDOMTest {
var reject = new RejectAllValidator<String>();
reject.setId("reject");
reject.initialize();
- var collect = CollectingValidator.<String>getInstance("collect");
+ var collect = CapturingValidator.<String>getInstance("collect");
var validators = new ArrayList<Validator<String>>();
validators.add(collect);
validators.add(reject);
@@ -96,10 +96,10 @@ public class StringAttributeValidationStageTest extends BaseDOMTest {
stage.destroy();
reject.destroy();
- var values = collect.getValues();
+ var values = collect.getCaptures();
collect.destroy();
Assert.assertEquals(values.size(), 1);
- Assert.assertEquals(values.get(0), "a");
+ Assert.assertEquals(values.get(0).value(), "a");
var errors = item.getItemMetadata().get(ErrorStatus.class);
Assert.assertEquals(errors.size(), 1);
@@ -140,7 +140,7 @@ public class StringAttributeValidationStageTest extends BaseDOMTest {
attributes.add(new QName("index"));
attributes.add(XMLConstants.XML_LANG_ATTRIB_NAME);
- var collect = CollectingValidator.<String>getInstance("collect");
+ var collect = CapturingValidator.<String>getInstance("collect");
var validators = new ArrayList<Validator<String>>();
validators.add(collect);
@@ -154,16 +154,16 @@ public class StringAttributeValidationStageTest extends BaseDOMTest {
stage.destroy();
- var values = collect.getValues();
+ var values = collect.getCaptures();
collect.destroy();
System.out.println(values);
Assert.assertEquals(values.size(), 18);
// Count how many "en"s there are
- Assert.assertEquals(values.stream().filter(x -> x.equals("en")).count(), 3);
+ Assert.assertEquals(values.stream().filter(x -> x.value().equals("en")).count(), 3);
// Count how many "shibboleth.net"s there are
- Assert.assertEquals(values.stream().filter(x -> x.contains("shibboleth.net")).count(), 3);
+ Assert.assertEquals(values.stream().filter(x -> x.value().contains("shibboleth.net")).count(), 3);
// Count how many indexes (matching single digits) there are
- Assert.assertEquals(values.stream().filter(x -> x.matches("\\d")).count(), 12);
+ Assert.assertEquals(values.stream().filter(x -> x.value().matches("\\d")).count(), 12);
}
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/testing/BaseTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/testing/BaseTest.java
index c208594..0a818d3 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/testing/BaseTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/testing/BaseTest.java
@@ -20,7 +20,10 @@ import javax.annotation.Nonnull;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
+import org.testng.Assert;
+import net.shibboleth.metadata.ErrorStatus;
+import net.shibboleth.metadata.Item;
import net.shibboleth.shared.logic.Constraint;
public abstract class BaseTest {
@@ -131,4 +134,18 @@ public abstract class BaseTest {
}
}
+ /**
+ * Extract a given {@link ErrorStatus} from an {@link Item}
+ *
+ * @param item {@link Item} from which the {@link ErrorStatus} should be extracted
+ * @param index index of the {@link ErrorStatus} to be extracted
+ *
+ * @return the extracted {@link ErrorStatus}
+ */
+ protected ErrorStatus extractError(final @Nonnull Item<?> item, int index) {
+ final var errors = item.getItemMetadata().get(ErrorStatus.class);
+ Assert.assertTrue(errors.size() > index, "indexed error status not present");
+ return errors.get(index);
+ }
+
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseAsValidatorTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseAsValidatorTest.java
index 98e0be0..7cce5d3 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseAsValidatorTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseAsValidatorTest.java
@@ -11,36 +11,15 @@ import org.testng.annotations.Test;
import net.shibboleth.metadata.ErrorStatus;
import net.shibboleth.metadata.testing.MockItem;
import net.shibboleth.metadata.validate.Validator.Action;
+import net.shibboleth.metadata.validate.testing.BoomAsValidator;
import net.shibboleth.metadata.validate.testing.BoomValidator;
+import net.shibboleth.metadata.validate.testing.CapturingValidator;
+import net.shibboleth.metadata.validate.testing.PassThroughAsValidator;
+import net.shibboleth.shared.collection.CollectionSupport;
public class BaseAsValidatorTest {
- /**
- * Test an "as" validator which always fails to convert.
- */
- private class BoomAsValidator extends BaseAsValidator<String, String> {
-
- @Override
- protected @Nonnull String convert(@Nonnull String from) throws IllegalArgumentException {
- if (from.equals("nothing")) {
- throw new IllegalArgumentException();
- } else {
- throw new IllegalArgumentException("something");
- }
- }
-
- }
-
- private class PassThroughAsValidator<T> extends BaseAsValidator<T, T> {
-
- @Override
- protected @Nonnull T convert(@Nonnull T from) throws IllegalArgumentException {
- return from;
- }
-
- }
-
@Test
public void convertTestNoDetail() throws Exception {
@@ -98,4 +77,21 @@ public class BaseAsValidatorTest {
Assert.assertEquals(errors.get(0).getComponentId(), "outer/passer/boomer");
Assert.assertEquals(errors.get(0).getStatusMessage(), "value rejected: 'value'");
}
+
+ @Test
+ public void testValueContext() throws Exception {
+ final var collector = CapturingValidator.<String>getInstance("capture");
+ final var item = new MockItem("content");
+ final var passer = new PassThroughAsValidator<String>();
+ passer.setId("passer");
+ passer.setValidators(CollectionSupport.singletonList(collector));
+ passer.initialize();
+ passer.validate("value1", item, "caller1");
+ passer.validate("value2", item, "caller2", "value-context");
+ final var c = collector.getCaptures();
+ Assert.assertEquals(c.size(), 2);
+ Assert.assertEquals(c.get(0).valueContext(), "value1"); // implicit
+ Assert.assertEquals(c.get(1).valueContext(), "value-context"); // explicit
+ passer.destroy();
+ }
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseValidatorTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseValidatorTest.java
index a6242b3..bcc92a7 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseValidatorTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/BaseValidatorTest.java
@@ -12,6 +12,9 @@ import net.shibboleth.metadata.testing.MockItem;
import net.shibboleth.metadata.validate.Validator.Action;
import net.shibboleth.metadata.validate.testing.BoomValidator;
+/**
+ * Tests for the {@link BaseValidator} class.
+ */
public class BaseValidatorTest {
@Test
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/ValidatorSequenceTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/ValidatorSequenceTest.java
index 4d4d9e6..27af8d6 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/ValidatorSequenceTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/ValidatorSequenceTest.java
@@ -11,6 +11,8 @@ import net.shibboleth.metadata.ErrorStatus;
import net.shibboleth.metadata.Item;
import net.shibboleth.metadata.testing.MockItem;
import net.shibboleth.metadata.validate.Validator.Action;
+import net.shibboleth.metadata.validate.testing.CapturingValidator;
+import net.shibboleth.shared.collection.CollectionSupport;
public class ValidatorSequenceTest {
@@ -85,4 +87,20 @@ public class ValidatorSequenceTest {
Assert.assertEquals(err.getComponentId(), "stage/reject");
}
+ @Test
+ public void testValueContext() throws Exception {
+ final var collector = CapturingValidator.<String>getInstance("capture");
+ final var v = new ValidatorSequence<String>();
+ v.setId("seq");
+ v.setValidators(CollectionSupport.singletonList(collector));
+ v.initialize();
+ final var item = new MockItem("content");
+ v.validate("value", item, "caller1");
+ v.validate("value2", item, "caller2", "value-context");
+ final var c = collector.getCaptures();
+ Assert.assertEquals(c.size(), 2);
+ Assert.assertNull(c.get(0).valueContext());
+ Assert.assertEquals(c.get(1).valueContext(), "value-context");
+ v.destroy();
+ }
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/string/AsURLStringValidatorTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/string/AsURLStringValidatorTest.java
index 2fc812f..9aebb1c 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/string/AsURLStringValidatorTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/string/AsURLStringValidatorTest.java
@@ -8,12 +8,18 @@ import org.testng.Assert;
import org.testng.annotations.Test;
import net.shibboleth.metadata.ErrorStatus;
+import net.shibboleth.metadata.testing.BaseTest;
import net.shibboleth.metadata.testing.MockItem;
import net.shibboleth.metadata.validate.Validator.Action;
-import net.shibboleth.metadata.validate.testing.CollectingValidator;
+import net.shibboleth.metadata.validate.testing.CapturingValidator;
+import net.shibboleth.metadata.validate.url.MissingHostURLValidator;
import net.shibboleth.shared.collection.CollectionSupport;
-public class AsURLStringValidatorTest {
+public class AsURLStringValidatorTest extends BaseTest {
+
+ public AsURLStringValidatorTest() {
+ super(AsURLStringValidator.class);
+ }
/**
* Generic test for a good URL.
@@ -23,7 +29,7 @@ public class AsURLStringValidatorTest {
* @throws Exception if something goes wrong
*/
private @Nonnull URL good(@Nonnull final String value) throws Exception {
- final var cv = CollectingValidator.<URL>getInstance("collect");
+ final var cv = CapturingValidator.<URL>getInstance("collect");
final var item = new MockItem("item");
final var v = new AsURLStringValidator();
v.setId("test");
@@ -32,9 +38,9 @@ public class AsURLStringValidatorTest {
var result = v.validate(value, item, "stage");
Assert.assertEquals(result, Action.CONTINUE);
Assert.assertTrue(item.getItemMetadata().isEmpty());
- var collected = cv.getValues();
- Assert.assertEquals(collected.size(), 1);
- var url = collected.get(0);
+ var captures = cv.getCaptures();
+ Assert.assertEquals(captures.size(), 1);
+ var url = captures.get(0).value();
cv.destroy();
v.destroy();
assert url != null;
@@ -91,4 +97,53 @@ public class AsURLStringValidatorTest {
badURL("http://*** FILL IN ***/");
}
+ @Test
+ public void testImplicitValueContext() throws Exception {
+ final var item = new MockItem("item");
+
+ final var vv = new MissingHostURLValidator();
+ vv.setId("host");
+ vv.initialize();
+
+ final var v = new AsURLStringValidator();
+ v.setId("test");
+ v.setValidators(CollectionSupport.listOf(vv));
+ v.initialize();
+
+ final var result = v.validate("http:///example", item, "caller");
+ Assert.assertEquals(result, Action.DONE);
+
+ final var message = extractError(item, 0).getStatusMessage();
+ Assert.assertEquals(message, "'http:///example': host name not present");
+
+ final var result2 = v.validate("http:///example", item, "caller", null);
+ Assert.assertEquals(result2, Action.DONE);
+
+ final var message2 = extractError(item, 1).getStatusMessage();
+ Assert.assertEquals(message2, "'http:///example': host name not present");
+
+ v.destroy();
+ }
+
+ @Test
+ public void textExplicitValueContext() throws Exception {
+ final var item = new MockItem("item");
+
+ final var vv = new MissingHostURLValidator();
+ vv.setId("host");
+ vv.initialize();
+
+ final var v = new AsURLStringValidator();
+ v.setId("test");
+ v.setValidators(CollectionSupport.listOf(vv));
+ v.initialize();
+
+ final var result = v.validate("http:///example", item, "caller", "explicit-context");
+ Assert.assertEquals(result, Action.DONE);
+
+ final var message = extractError(item, 0).getStatusMessage();
+ Assert.assertEquals(message, "'explicit-context': host name not present");
+
+ v.destroy();
+ }
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/BoomAsValidator.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/BoomAsValidator.java
new file mode 100644
index 0000000..4d93d07
--- /dev/null
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/BoomAsValidator.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.metadata.validate.testing;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.metadata.validate.BaseAsValidator;
+
+/**
+ * An "as" validator which always fails to convert.
+ */
+public class BoomAsValidator extends BaseAsValidator<String, String> {
+
+ @Override
+ protected @Nonnull String convert(@Nonnull String from) throws IllegalArgumentException {
+ if (from.equals("nothing")) {
+ throw new IllegalArgumentException();
+ } else {
+ throw new IllegalArgumentException("something");
+ }
+ }
+
+}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/CollectingValidator.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/CapturingValidator.java
similarity index 59%
rename from mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/CollectingValidator.java
rename to mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/CapturingValidator.java
index 7de5cfe..f5da24a 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/CollectingValidator.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/CapturingValidator.java
@@ -18,6 +18,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.shibboleth.metadata.Item;
import net.shibboleth.metadata.pipeline.StageProcessingException;
@@ -26,7 +27,7 @@ import net.shibboleth.metadata.validate.Validator;
import net.shibboleth.shared.component.ComponentInitializationException;
/**
- * A {@link Validator} implementation which collects the values passed to it for
+ * A {@link Validator} implementation which captures the values passed to it for
* validation.
*
* <p>This can be used in tests to record the nodes which are visited, and the
@@ -34,18 +35,28 @@ import net.shibboleth.shared.component.ComponentInitializationException;
*
* @param <T> type of the values to be validated
*/
-public class CollectingValidator<T> extends BaseValidator implements Validator<T> {
+public class CapturingValidator<T> extends BaseValidator implements Validator<T> {
- /** Values this validator has seen, in order. */
- private final @Nonnull List<T> values = new ArrayList<>();
+ /**
+ * Represents a captured validation.
+ *
+ * @param value the value to be validated
+ * @param valueContext the value context, or {@code null}
+ * @param <T> type of the values to be validated
+ */
+ public record Capture<T>(@Nonnull T value, @Nullable String valueContext) {
+ }
+
+ /** Validations this validator has seen, in order. */
+ private final @Nonnull List<Capture<T>> captures = new ArrayList<>();
/**
- * Return the values recorded by this validator.
+ * Return the values captured by this validator.
*
- * @return the values recorded by this validator
+ * @return the values captured by this validator
*/
- public @Nonnull List<T> getValues() {
- return values;
+ public @Nonnull List<Capture<T>> getCaptures() {
+ return captures;
}
/**
@@ -56,18 +67,18 @@ public class CollectingValidator<T> extends BaseValidator implements Validator<T
* @return new validator instance
* @throws ComponentInitializationException if something goes wrong in initialisation
*/
- public static @Nonnull <TT> CollectingValidator<TT> getInstance(final @Nonnull String id)
+ public static @Nonnull <TT> CapturingValidator<TT> getInstance(final @Nonnull String id)
throws ComponentInitializationException {
- final var instance = new CollectingValidator<TT>();
+ final var instance = new CapturingValidator<TT>();
instance.setId(id);
instance.initialize();
return instance;
}
@Override
- public @Nonnull Action validate(@Nonnull T e, @Nonnull Item<?> item, @Nonnull String callerId)
- throws StageProcessingException {
- values.add(e);
+ public @Nonnull Action validate(final @Nonnull T e, final @Nonnull Item<?> item, final @Nonnull String callerId,
+ final @Nullable String valueContext) throws StageProcessingException {
+ captures.add(new Capture<T>(e, valueContext));
return Action.CONTINUE;
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/PassThroughAsValidator.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/PassThroughAsValidator.java
new file mode 100644
index 0000000..615d6dd
--- /dev/null
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/testing/PassThroughAsValidator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.metadata.validate.testing;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.metadata.validate.BaseAsValidator;
+
+/**
+ * An "as" validator which simply passes through instead of converting.
+ *
+ * @param <T> the type to pass through
+ */
+public class PassThroughAsValidator<T> extends BaseAsValidator<T, T> {
+
+ @Override
+ protected @Nonnull T convert(@Nonnull T from) throws IllegalArgumentException {
+ return from;
+ }
+
+}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidatorTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidatorTest.java
index 9256d4f..3afdfd4 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidatorTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/EmptyPortURLValidatorTest.java
@@ -5,18 +5,37 @@ import java.net.URL;
import org.testng.Assert;
import org.testng.annotations.Test;
+import net.shibboleth.metadata.testing.BaseTest;
import net.shibboleth.metadata.testing.MockItem;
import net.shibboleth.metadata.validate.Validator.Action;
-public class EmptyPortURLValidatorTest {
+public class EmptyPortURLValidatorTest extends BaseTest {
+
+ public EmptyPortURLValidatorTest() {
+ super(EmptyPortURLValidator.class);
+ }
+
@Test
public void testEmptyAuthority() throws Exception {
+ final var item = new MockItem("item");
+
final var val = new EmptyPortURLValidator();
val.setId("test");
val.initialize();
- final var item = new MockItem("item");
- final var res = val.validate(new URL("https:///whatever"), item, "stage");
- Assert.assertEquals(res, Action.CONTINUE);
+
+ final var res = val.validate(new URL("https://domain:/whatever"), item, "stage");
+ Assert.assertEquals(res, Action.DONE);
+
+ final var message = extractError(item, 0).getStatusMessage();
+ Assert.assertEquals(message, "libxml2: port present but empty");
+
+ final var res2 = val.validate(new URL("https://domain:/whatever"), item, "stage", "context");
+ Assert.assertEquals(res2, Action.DONE);
+
+ final var message2 = extractError(item, 1).getStatusMessage();
+ Assert.assertEquals(message2, "'context': libxml2: port present but empty");
+
val.destroy();
}
+
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidatorTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidatorTest.java
index d96be44..d657ae2 100644
--- a/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidatorTest.java
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/HTTPSProtocolURLValidatorTest.java
@@ -6,10 +6,15 @@ import org.testng.Assert;
import org.testng.annotations.Test;
import net.shibboleth.metadata.ErrorStatus;
+import net.shibboleth.metadata.testing.BaseTest;
import net.shibboleth.metadata.testing.MockItem;
import net.shibboleth.metadata.validate.Validator.Action;
-public class HTTPSProtocolURLValidatorTest {
+public class HTTPSProtocolURLValidatorTest extends BaseTest {
+
+ public HTTPSProtocolURLValidatorTest() {
+ super(HTTPSProtocolURLValidator.class);
+ }
@Test
public void testHttps() throws Exception {
@@ -51,4 +56,21 @@ public class HTTPSProtocolURLValidatorTest {
Assert.assertEquals(msg, "protocol 'http' must be https");
}
+ @Test
+ public void testValueContext() throws Exception {
+ final var item = new MockItem("data");
+
+ final var val = new HTTPSProtocolURLValidator();
+ val.setId("test");
+ val.initialize();
+
+ final var res = val.validate(new URL("http://example.com/"), item, "stage", "context");
+ Assert.assertEquals(res, Action.DONE);
+
+ final var errors = item.getItemMetadata().get(ErrorStatus.class);
+ Assert.assertEquals(errors.size(), 1);
+
+ final var message = extractError(item, 0).getStatusMessage();
+ Assert.assertEquals(message, "'context': protocol 'http' must be https");
+ }
}
diff --git a/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/MissingHostURLValidatorTest.java b/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/MissingHostURLValidatorTest.java
new file mode 100644
index 0000000..fe4b5f9
--- /dev/null
+++ b/mda-framework/src/test/java/net/shibboleth/metadata/validate/url/MissingHostURLValidatorTest.java
@@ -0,0 +1,41 @@
+package net.shibboleth.metadata.validate.url;
+
+import java.net.URL;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.metadata.testing.BaseTest;
+import net.shibboleth.metadata.testing.MockItem;
+import net.shibboleth.metadata.validate.Validator.Action;
+
+public class MissingHostURLValidatorTest extends BaseTest {
+
+ public MissingHostURLValidatorTest() {
+ super(MissingHostURLValidator.class);
+ }
+
+ @Test
+ public void testMissingHost() throws Exception {
+ final var item = new MockItem("item");
+
+ final var val = new MissingHostURLValidator();
+ val.setId("test");
+ val.initialize();
+
+ final var res = val.validate(new URL("https:///whatever"), item, "stage");
+ Assert.assertEquals(res, Action.DONE);
+
+ final var message = extractError(item, 0).getStatusMessage();
+ Assert.assertEquals(message, "host name not present");
+
+ final var res2 = val.validate(new URL("https:///whatever"), item, "stage", "context");
+ Assert.assertEquals(res2, Action.DONE);
+
+ final var message2 = extractError(item, 1).getStatusMessage();
+ Assert.assertEquals(message2, "'context': host name not present");
+
+ val.destroy();
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list