Skip to content

Commit 904e4c6

Browse files
authored
Record resolution errors for control panel (#463)
With this change, the control panel will now remember resolution errors. This can be particularly useful when resolution fails on the client side, and that the user doesn't really understand why. By going to the control panel, the errors will be recorded per resolver.
1 parent 909f3c5 commit 904e4c6

File tree

8 files changed

+185
-24
lines changed

8 files changed

+185
-24
lines changed

Diff for: test-resources-control-panel/src/main/java/io/micronaut/testresources/controlpanel/ControlPanelPropertyResolutionListener.java

+50
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import io.micronaut.testresources.server.PropertyResolutionListener;
2121
import jakarta.inject.Singleton;
2222

23+
import java.io.PrintWriter;
24+
import java.io.StringWriter;
2325
import java.util.Comparator;
2426
import java.util.HashMap;
2527
import java.util.HashSet;
@@ -36,6 +38,7 @@
3638
@Singleton
3739
public class ControlPanelPropertyResolutionListener implements PropertyResolutionListener {
3840
private final Map<String, Set<Resolution>> resolvedProperties = new HashMap<>();
41+
private final Map<String, Set<ResolutionError>> errors = new HashMap<>();
3942

4043
@Override
4144
public void resolved(String property, String resolvedValue, TestResourcesResolver resolver,
@@ -50,6 +53,13 @@ public void resolved(String property, String resolvedValue, TestResourcesResolve
5053
.add(resolution);
5154
}
5255

56+
@Override
57+
public void errored(String property, TestResourcesResolver resolver, Throwable exception) {
58+
var error = ResolutionError.of(property, exception);
59+
errors.computeIfAbsent(resolver.getId(), k -> new HashSet<>())
60+
.add(error);
61+
}
62+
5363
/**
5464
* Returns the resolutions performed by a particular resolver.
5565
* @param resolver the resolver
@@ -71,6 +81,18 @@ public List<Resolution> findById(String id) {
7181
.toList();
7282
}
7383

84+
/**
85+
* Returns the resolutions performed by a particular resolver.
86+
* @param id the resolver id
87+
* @return the resolutions
88+
*/
89+
public List<ResolutionError> findErrorsById(String id) {
90+
return errors.getOrDefault(id, Set.of())
91+
.stream()
92+
.sorted(Comparator.comparing(ResolutionError::property))
93+
.toList();
94+
}
95+
7496
private static Map<String, String> asStringMap(Map<String, Object> input) {
7597
return input.entrySet()
7698
.stream()
@@ -82,6 +104,7 @@ private static Map<String, String> asStringMap(Map<String, Object> input) {
82104

83105
/**
84106
* A property resolution.
107+
*
85108
* @param property the property which was resolved
86109
* @param resolvedValue the resolved value as a string
87110
* @param properties the properties which were used for the resolution context
@@ -96,4 +119,31 @@ public record Resolution(
96119
) {
97120

98121
}
122+
123+
/**
124+
* A property resolution error.
125+
*
126+
* @param property the property which was resolved
127+
* @param stackTrace the associated resolution error
128+
*/
129+
@Introspected
130+
public record ResolutionError(
131+
String property,
132+
String stackTrace
133+
) {
134+
135+
/**
136+
* Builds a resolution error.
137+
* @param property the property
138+
* @param exception the exception
139+
* @return a resolution error
140+
*/
141+
public static ResolutionError of(String property, Throwable exception) {
142+
var stackTrace = new StringWriter();
143+
try (var writer = new PrintWriter(stackTrace)) {
144+
exception.printStackTrace(writer);
145+
}
146+
return new ResolutionError(property, stackTrace.toString());
147+
}
148+
}
99149
}

Diff for: test-resources-control-panel/src/main/java/io/micronaut/testresources/controlpanel/TestResourcesControlPanel.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@
1818
import io.micronaut.controlpanel.core.AbstractControlPanel;
1919
import io.micronaut.controlpanel.core.config.ControlPanelConfiguration;
2020

21-
import java.util.List;
22-
2321
/**
2422
* A control panel for test resource resolvers.
2523
* Each resolver will result in the creation of a separate control panel.
2624
* The control panel is responsible for showing the properties resolved
2725
* by this particular test resources resolver.
2826
*/
29-
public class TestResourcesControlPanel extends AbstractControlPanel<List<ControlPanelPropertyResolutionListener.Resolution>> {
27+
public class TestResourcesControlPanel extends AbstractControlPanel<TestResourcesControlPanelBody> {
3028
private final ControlPanelPropertyResolutionListener resolutionListener;
3129
private final String id;
3230

@@ -45,9 +43,16 @@ public String getBadge() {
4543
);
4644
}
4745

46+
public int getErrorCount() {
47+
return resolutionListener.findErrorsById(id).size();
48+
}
49+
4850
@Override
49-
public List<ControlPanelPropertyResolutionListener.Resolution> getBody() {
50-
return resolutionListener.findById(id);
51+
public TestResourcesControlPanelBody getBody() {
52+
return new TestResourcesControlPanelBody(
53+
resolutionListener.findById(id),
54+
resolutionListener.findErrorsById(id)
55+
);
5156
}
5257

5358
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2017-2023 original authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micronaut.testresources.controlpanel;
17+
18+
import io.micronaut.core.annotation.Introspected;
19+
20+
import java.util.List;
21+
22+
@Introspected
23+
public record TestResourcesControlPanelBody(
24+
List<ControlPanelPropertyResolutionListener.Resolution> resolvedProperties,
25+
List<ControlPanelPropertyResolutionListener.ResolutionError> errors
26+
) {
27+
}

Diff for: test-resources-control-panel/src/main/resources/views/test-resources/body.hbs

+9
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,14 @@
99
Resolved {{badge}} properties.
1010
{{/eq}}
1111
{{/eq}}
12+
{{#neq errorCount 0 }}
13+
<div class="card bg-danger">
14+
{{#eq errorCount 1}}
15+
1 resolution failure
16+
{{else}}
17+
{{errorCount}} resolution failures
18+
{{/eq}}
19+
</div>
20+
{{/neq}}
1221

1322
{{/with}}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<div class="card card-light">
2-
{{#neq controlPanel.body.size 0}}
3-
<div class="card-header">
4-
<h3 class="card-title"><code>{{@key}}</code></h3>
5-
</div>
6-
<div class="card-body">
2+
<div class="card-header">
3+
<h3 class="card-title"><code>{{@key}}</code></h3>
4+
</div>
5+
<div class="card-body">
6+
{{#neq controlPanel.body.resolvedProperties.size 0}}
77
<p>
88
The following table contains the properties which were resolved by this test resources provider.
99
</p>
@@ -17,7 +17,7 @@
1717
</tr>
1818
</thead>
1919
<tbody>
20-
{{#each controlPanel.body as |resolution|}}
20+
{{#each controlPanel.body.resolvedProperties as |resolution|}}
2121
<tr>
2222
<td><code>{{resolution.property}}</code></td>
2323
<td><code>{{resolution.resolvedValue}}</code></td>
@@ -28,8 +28,28 @@
2828
{{/each}}
2929
</tbody>
3030
</table>
31-
</div>
32-
{{else}}
33-
This test resources provider hasn't resolved any property yet.
34-
{{/neq}}
31+
{{else}}
32+
This test resources provider hasn't resolved any property yet.
33+
{{/neq}}
34+
{{#neq controlPanel.body.errors.size 0}}
35+
<p>There were errors during resolution of properties.</p>
36+
<table class="table">
37+
<thead>
38+
<tr>
39+
<th>Property</th>
40+
<th>Error</th>
41+
</tr>
42+
</thead>
43+
<tbody>
44+
{{#each controlPanel.body.errors as |error|}}
45+
<tr>
46+
<td><code>{{error.property}}</code></td>
47+
<td><code>{{error.stackTrace}}</code></td>
48+
</tr>
49+
{{/each}}
50+
</tbody>
51+
</table>
52+
{{/neq}}
53+
</div>
54+
3555
</div>

Diff for: test-resources-core/src/main/java/io/micronaut/testresources/core/TestResourcesResolutionException.java

+15
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ public TestResourcesResolutionException(String message, Throwable cause) {
3131
public TestResourcesResolutionException(String message) {
3232
super(message);
3333
}
34+
35+
/**
36+
* Returns a TestResourcesResolutionException from an exception. If
37+
* the type of the exception is already TestResourcesResolutionException
38+
* then the same instance is returned, otherwise the exception is
39+
* wrapped into TestResourcesResolutionException.
40+
* @param ex the exception
41+
* @return a TestResourcesResolutionException
42+
*/
43+
public static TestResourcesResolutionException wrap(Exception ex) {
44+
if (ex instanceof TestResourcesResolutionException trre) {
45+
return trre;
46+
}
47+
return new TestResourcesResolutionException(ex);
48+
}
3449
}

Diff for: test-resources-server/src/main/java/io/micronaut/testresources/server/PropertyResolutionListener.java

+11
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,15 @@ void resolved(String property,
3737
TestResourcesResolver resolver,
3838
Map<String, Object> properties,
3939
Map<String, Object> testResourcesConfig);
40+
41+
/**
42+
* Records an error which happened during property resolution,
43+
* for example if a container fails to start.
44+
* @param property the property which we attempted to resolve
45+
* @param resolver the resolver which failed
46+
* @param error the error which happened
47+
*/
48+
void errored(String property,
49+
TestResourcesResolver resolver,
50+
Throwable error);
4051
}

0 commit comments

Comments
 (0)