GH-995 - Allow filtering violations.

This commit is contained in:
Oliver Drotbohm
2024-12-18 19:17:01 +01:00
parent f1ba94989e
commit dca73ee137
4 changed files with 120 additions and 7 deletions

View File

@@ -40,7 +40,6 @@ import org.springframework.lang.Nullable;
import org.springframework.modulith.core.Types.JMoleculesTypes;
import org.springframework.modulith.core.Types.JavaTypes;
import org.springframework.modulith.core.Types.SpringTypes;
import org.springframework.modulith.core.Violations.Violation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.function.SingletonSupplier;

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.modulith.core;
import java.util.Objects;
import org.springframework.util.Assert;
/**
* An individual architectural violation.
*
* @author Oliver Drotbohm
* @since 1.3.1
*/
public class Violation {
final String message;
Violation(String message) {
this.message = message;
}
/**
* Returns the violation message.
*
* @return the message
*/
public String getMessage() {
return message;
}
/**
* Returns whether the {@link Violation}'s message contains the given candidate.
*
* @param candidate must not be {@literal null} or empty.
*/
public boolean hasMessageContaining(String candidate) {
Assert.hasText(candidate, "Candidate must not be null or empty!");
return message.contains(candidate);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return message;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return this == obj
|| obj instanceof Violation that
&& Objects.equals(this.message, that.message);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hashCode(message);
}
}

View File

@@ -20,6 +20,7 @@ import static java.util.stream.Collectors.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@@ -57,7 +58,7 @@ public class Violations extends RuntimeException {
* @return will never be {@literal null}.
*/
static Collector<String, ?, Violations> toViolations() {
return mapping(Violation::new, collectingAndThen(Collectors.toList(), Violations::new));
return mapping(Violation::new, newViolations());
}
/*
@@ -68,7 +69,7 @@ public class Violations extends RuntimeException {
public String getMessage() {
return violations.stream() //
.map(Violation::message) //
.map(Violation::getMessage) //
.collect(Collectors.joining("\n- ", "- ", ""));
}
@@ -81,7 +82,7 @@ public class Violations extends RuntimeException {
public List<String> getMessages() {
return violations.stream() //
.map(Violation::message)
.map(Violation::getMessage)
.toList();
}
@@ -104,6 +105,21 @@ public class Violations extends RuntimeException {
}
}
/**
* Returns {@link Violations} that match the given {@link Predicate}.
*
* @param filter must not be {@literal null}.
* @return
*/
public Violations filter(Predicate<Violation> filter) {
Assert.notNull(filter, "Filter must not be null!");
return violations.stream()
.filter(filter)
.collect(newViolations());
}
/**
* Creates a new {@link Violations} with the given {@link RuntimeException} added to the current ones?
*
@@ -142,14 +158,16 @@ public class Violations extends RuntimeException {
var result = new ArrayList<>(left);
var messages = left.stream().map(Violation::message).toList();
var messages = left.stream().map(Violation::getMessage).toList();
right.stream()
.filter(it -> !messages.contains(it.message()))
.filter(it -> !messages.contains(it.message))
.forEach(result::add);
return result.size() == left.size() ? left : result;
}
static record Violation(String message) {}
private static Collector<Violation, ?, Violations> newViolations() {
return collectingAndThen(Collectors.toList(), Violations::new);
}
}

View File

@@ -51,4 +51,14 @@ class ViolationsUnitTests {
assertThat(violations.getMessages())
.containsExactlyInAnyOrder("First", "Second", "Third");
}
@Test // GH-995
void allowsFilteringViolations() {
var violations = Violations.NONE.and("First").and("Second")
.filter(it -> it.hasMessageContaining("Sec"));
assertThat(violations.getMessages())
.containsExactly("Second");
}
}