Skip to content

Commit 3e69cc6

Browse files
authored
Provide convenience methods for SignPostingResult (#20)
Helps clients to branch based on result findings. Also related: a wrong method syntax is fixed in the README.md.
1 parent a4dd382 commit 3e69cc6

4 files changed

Lines changed: 261 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ ValidationResult result = webLinkProcessor.process(rawHeader);
9999
SignPostingProcessor processor = new SignPostingProcessor.Builder().build();
100100
SignPostingResult signPostResult = processor.process(result.weblinks());
101101

102-
if(signPostResult.containsIssues()){
102+
if(signPostResult.hasIssues()){
103103
// Retrieve the report
104104
var report = result.report();
105105
// Investigate the report

assets/compass-social-preview.png

-56.7 KB
Binary file not shown.

src/main/java/life/qbic/compass/model/SignPostingResult.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package life.qbic.compass.model;
22

3+
import java.util.Objects;
34
import life.qbic.linksmith.model.WebLink;
45
import life.qbic.linksmith.spi.WebLinkValidator.IssueReport;
56

@@ -51,6 +52,13 @@
5152
* <li>compose validation results in higher-level workflows.</li>
5253
* </ul>
5354
*
55+
* <h2>Null-handling and optional Level 2 view</h2>
56+
* <p>
57+
* {@link #level2LinksetView()} may be {@code null}. This is intentional: not every validation step
58+
* constructs a Level 2 interpretation. Use {@link #hasLinkSetView()} to check for presence, or wrap it
59+
* using {@code Optional.ofNullable(result.level2LinksetView())}.
60+
* </p>
61+
*
5462
* @param signPostingView a read-only view on the validated weblinks
5563
* @param issueReport an aggregated report of all recoded issues during validation
5664
* @param level2LinksetView a Signposting Level 2 compliant view semantics in case the validator
@@ -64,6 +72,47 @@ public record SignPostingResult(
6472
IssueReport issueReport,
6573
Level2LinksetView level2LinksetView) {
6674

75+
public SignPostingResult {
76+
Objects.requireNonNull(signPostingView);
77+
Objects.requireNonNull(issueReport);
78+
}
79+
80+
/**
81+
* Creates a result without a Level 2 Link Set view.
82+
*
83+
* <p>
84+
* Use this factory when validation did not (or must not) produce a {@link Level2LinksetView}.
85+
* The returned result will have {@link #level2LinksetView()} set to {@code null}.
86+
* </p>
87+
*
88+
* @param signPostingView a read-only view on the validated weblinks (never {@code null})
89+
* @param issueReport an aggregated report of all recorded issues during validation (never {@code null})
90+
* @return a {@code SignPostingResult} with no Level 2 view
91+
* @since 1.0.0
92+
*/
93+
public static SignPostingResult withoutLinksetView(SignPostingView signPostingView, IssueReport issueReport) {
94+
return new SignPostingResult(signPostingView, issueReport, null);
95+
}
96+
97+
/**
98+
* Creates a result with a non-null Level 2 Link Set view.
99+
*
100+
* @param signPostingView a read-only view on the validated weblinks (never {@code null})
101+
* @param issueReport an aggregated report of all recorded issues during validation (never {@code null})
102+
* @param level2LinksetView a Level 2 interpretation view (must not be {@code null})
103+
* @return a {@code SignPostingResult} with a Level 2 view attached
104+
* @throws NullPointerException if {@code level2LinksetView} is {@code null}
105+
* @since 1.0.0
106+
*/
107+
public static SignPostingResult withLinksetView(
108+
SignPostingView signPostingView,
109+
IssueReport issueReport,
110+
Level2LinksetView level2LinksetView
111+
) {
112+
Objects.requireNonNull(level2LinksetView, "level2LinksetView");
113+
return new SignPostingResult(signPostingView, issueReport, level2LinksetView);
114+
}
115+
67116
/**
68117
* Convenience method for aggregators or filters to check, if the current SignPosting result
69118
* contains a linkset view or not.
@@ -73,4 +122,35 @@ public record SignPostingResult(
73122
public boolean hasLinkSetView() {
74123
return level2LinksetView != null;
75124
}
125+
126+
/**
127+
* Convenience method to check if any issues (warnings or errors) were recorded.
128+
*
129+
* @return true if the {@link #issueReport()} contains any issues
130+
* @since 1.0.0
131+
*/
132+
public boolean hasIssues() {
133+
return !issueReport.issues().isEmpty();
134+
}
135+
136+
/**
137+
* Convenience method to check if any errors were recorded.
138+
*
139+
* @return true if the {@link #issueReport()} contains errors
140+
* @since 1.0.0
141+
*/
142+
public boolean hasErrors() {
143+
return issueReport.hasErrors();
144+
}
145+
146+
/**
147+
* Convenience method to check if any warnings were recorded.
148+
*
149+
* @return true if the {@link #issueReport()} contains warnings
150+
* @since 1.0.0
151+
*/
152+
public boolean hasWarnings() {
153+
return issueReport.hasWarnings();
154+
}
155+
76156
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package life.qbic.compass.model
2+
3+
import spock.lang.Specification
4+
import spock.lang.Unroll
5+
6+
class SignPostingResultSpec extends Specification {
7+
8+
@Unroll
9+
def "stable API: method '#methodName' is available with return type '#returnType.simpleName' and params #paramTypes*.simpleName"() {
10+
when:
11+
def method = SignPostingResult.getMethod(methodName, paramTypes as Class[])
12+
13+
then:
14+
method != null
15+
method.returnType == returnType
16+
17+
where:
18+
methodName | returnType | paramTypes
19+
"signPostingView" | SignPostingView | ([] as Class[])
20+
"issueReport" | life.qbic.linksmith.spi.WebLinkValidator.IssueReport | ([] as Class[])
21+
"level2LinksetView" | Level2LinksetView | ([] as Class[])
22+
"hasLinkSetView" | boolean | ([] as Class[])
23+
"hasIssues" | boolean | ([] as Class[])
24+
"hasErrors" | boolean | ([] as Class[])
25+
"hasWarnings" | boolean | ([] as Class[])
26+
"withoutLinksetView" | SignPostingResult | ([SignPostingView, life.qbic.linksmith.spi.WebLinkValidator.IssueReport] as Class[])
27+
"withLinksetView" | SignPostingResult | ([SignPostingView, life.qbic.linksmith.spi.WebLinkValidator.IssueReport, Level2LinksetView] as Class[])
28+
}
29+
30+
def "stable API: canonical record constructor is available"() {
31+
when:
32+
def ctor = SignPostingResult.getDeclaredConstructor(
33+
SignPostingView,
34+
life.qbic.linksmith.spi.WebLinkValidator.IssueReport,
35+
Level2LinksetView
36+
)
37+
38+
then:
39+
ctor != null
40+
}
41+
42+
def "stable API: record fundamental methods exist"() {
43+
expect:
44+
SignPostingResult.getMethod("equals", Object) != null
45+
SignPostingResult.getMethod("hashCode") != null
46+
SignPostingResult.getMethod("toString") != null
47+
}
48+
49+
def "behavior: hasLinkSetView returns false when level2LinksetView is null"() {
50+
given:
51+
def view = new SignPostingView([])
52+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
53+
54+
and:
55+
def result = new SignPostingResult(view, report, null)
56+
57+
expect:
58+
!result.hasLinkSetView()
59+
}
60+
61+
def "behavior: hasLinkSetView returns true when level2LinksetView is present"() {
62+
given:
63+
def view = new SignPostingView([])
64+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
65+
66+
and:
67+
def nonNullLevel2View = linkSetView()
68+
69+
and:
70+
def result = new SignPostingResult(view, report, nonNullLevel2View)
71+
72+
expect:
73+
result.hasLinkSetView()
74+
}
75+
76+
@Unroll
77+
def "behavior: hasLinkSetView is consistent with null-check (#caseName)"(String caseName, Level2LinksetView level2View) {
78+
given:
79+
def view = new SignPostingView([])
80+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
81+
82+
and:
83+
def result = new SignPostingResult(view, report, level2View)
84+
85+
expect:
86+
result.hasLinkSetView() == (result.level2LinksetView() != null)
87+
88+
where:
89+
caseName | level2View
90+
"null view" | null
91+
"non-null view" | linkSetView()
92+
}
93+
94+
def "behavior: hasIssues/hasErrors/hasWarnings are all false when IssueReport is empty"() {
95+
given:
96+
def view = new SignPostingView([])
97+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
98+
99+
and:
100+
def result = new SignPostingResult(view, report, null)
101+
102+
expect:
103+
!result.hasIssues()
104+
!result.hasErrors()
105+
!result.hasWarnings()
106+
}
107+
108+
def "behavior: hasWarnings true implies hasIssues true (warnings are issues)"() {
109+
given:
110+
def view = new SignPostingView([])
111+
def warning = life.qbic.linksmith.spi.WebLinkValidator.Issue.warning("w1")
112+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([warning])
113+
114+
and:
115+
def result = new SignPostingResult(view, report, null)
116+
117+
expect:
118+
result.hasWarnings()
119+
!result.hasErrors()
120+
result.hasIssues()
121+
}
122+
123+
def "behavior: hasErrors true implies hasIssues true"() {
124+
given:
125+
def view = new SignPostingView([])
126+
def error = life.qbic.linksmith.spi.WebLinkValidator.Issue.error("e1")
127+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([error])
128+
129+
and:
130+
def result = new SignPostingResult(view, report, null)
131+
132+
expect:
133+
!result.hasWarnings()
134+
result.hasErrors()
135+
result.hasIssues()
136+
}
137+
138+
def "behavior: withoutLinksetView creates a result with null level2LinksetView"() {
139+
given:
140+
def view = new SignPostingView([])
141+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
142+
143+
when:
144+
def result = SignPostingResult.withoutLinksetView(view, report)
145+
146+
then:
147+
result.level2LinksetView() == null
148+
!result.hasLinkSetView()
149+
}
150+
151+
def "behavior: withLinksetView rejects null level2LinksetView"() {
152+
given:
153+
def view = new SignPostingView([])
154+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
155+
156+
when:
157+
SignPostingResult.withLinksetView(view, report, null)
158+
159+
then:
160+
thrown(NullPointerException)
161+
}
162+
163+
def "behavior: withLinksetView creates a result with non-null level2LinksetView"() {
164+
given:
165+
def view = new SignPostingView([])
166+
def report = new life.qbic.linksmith.spi.WebLinkValidator.IssueReport([])
167+
def level2 = linkSetView()
168+
169+
when:
170+
def result = SignPostingResult.withLinksetView(view, report, level2)
171+
172+
then:
173+
result.level2LinksetView().is(level2)
174+
result.hasLinkSetView()
175+
}
176+
177+
private static Level2LinksetView linkSetView() {
178+
return new Level2LinksetView([], [], [], [])
179+
}
180+
}

0 commit comments

Comments
 (0)