Skip to content

Commit b48c052

Browse files
committed
ProblemDetails to interface in API module
1 parent d4285a1 commit b48c052

File tree

5 files changed

+103
-57
lines changed

5 files changed

+103
-57
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Inrupt Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal in
6+
* the Software without restriction, including without limitation the rights to use,
7+
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
8+
* Software, and to permit persons to whom the Software is furnished to do so,
9+
* subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
package com.inrupt.client;
22+
23+
import java.net.URI;
24+
25+
/**
26+
* A data class representing a structured problem description sent by the server on error response.
27+
*
28+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC 9457 Problem Details for HTTP APIs</a>
29+
*/
30+
public interface ProblemDetails {
31+
32+
/**
33+
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default MIME type.
34+
*/
35+
String MIME_TYPE = "application/problem+json";
36+
37+
/**
38+
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default problem type.
39+
*/
40+
String DEFAULT_TYPE = "about:blank";
41+
42+
/**
43+
* The problem type.
44+
* @return the type
45+
*/
46+
URI getType();
47+
48+
/**
49+
* The problem title.
50+
* @return the title
51+
*/
52+
String getTitle();
53+
54+
/**
55+
* The problem details.
56+
* @return the details
57+
*/
58+
String getDetails();
59+
60+
/**
61+
* The problem status code.
62+
* @return the status code
63+
*/
64+
int getStatus();
65+
66+
/**
67+
* The problem instance.
68+
* @return the instance
69+
*/
70+
URI getInstance();
71+
}

solid/src/main/java/com/inrupt/client/solid/SolidClientException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.inrupt.client.Headers;
2424
import com.inrupt.client.InruptClientException;
25+
import com.inrupt.client.ProblemDetails;
2526

2627
import java.net.URI;
2728

@@ -96,7 +97,7 @@ public String getBody() {
9697
* @return the problem details object
9798
*/
9899
public ProblemDetails getProblemDetails() {
99-
return ProblemDetails.fromErrorResponse(statusCode, headers, body.getBytes());
100+
return SolidProblemDetails.fromErrorResponse(statusCode, headers, body.getBytes());
100101
}
101102

102103
/**

solid/src/main/java/com/inrupt/client/solid/ProblemDetails.java renamed to solid/src/main/java/com/inrupt/client/solid/SolidProblemDetails.java

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package com.inrupt.client.solid;
2222

2323
import com.inrupt.client.Headers;
24+
import com.inrupt.client.ProblemDetails;
2425
import com.inrupt.client.spi.JsonService;
2526
import com.inrupt.client.spi.ServiceProvider;
2627

@@ -29,23 +30,10 @@
2930
import java.net.URI;
3031
import java.util.Optional;
3132

32-
/**
33-
* A data class representing a structured problem description sent by the server on error response.
34-
*
35-
* @see <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC 9457 Problem Details for HTTP APIs</a>
36-
*/
37-
public class ProblemDetails {
33+
public class SolidProblemDetails implements ProblemDetails {
3834

3935
private static final long serialVersionUID = -4597170432270957765L;
4036

41-
/**
42-
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default MIME type.
43-
*/
44-
public static final String MIME_TYPE = "application/problem+json";
45-
/**
46-
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default problem type.
47-
*/
48-
public static final String DEFAULT_TYPE = "about:blank";
4937
private final URI type;
5038
private final String title;
5139
private final String details;
@@ -63,7 +51,7 @@ public class ProblemDetails {
6351
* @param status the error response status code
6452
* @param instance the problem instance
6553
*/
66-
public ProblemDetails(
54+
public SolidProblemDetails(
6755
final URI type,
6856
final String title,
6957
final String details,
@@ -77,50 +65,35 @@ public ProblemDetails(
7765
this.instance = instance;
7866
}
7967

80-
/**
81-
* The problem type.
82-
* @return the type
83-
*/
68+
@Override
8469
public URI getType() {
8570
return this.type;
8671
}
8772

88-
/**
89-
* The problem title.
90-
* @return the title
91-
*/
73+
@Override
9274
public String getTitle() {
9375
return this.title;
9476
}
9577

96-
/**
97-
* The problem details.
98-
* @return the details
99-
*/
78+
@Override
10079
public String getDetails() {
10180
return this.details;
10281
}
10382

104-
/**
105-
* The problem status code.
106-
* @return the status code
107-
*/
83+
@Override
10884
public int getStatus() {
10985
return this.status;
11086
}
11187

112-
/**
113-
* The problem instance.
114-
* @return the instance
115-
*/
88+
@Override
11689
public URI getInstance() {
11790
return this.instance;
11891
}
11992

12093
/**
12194
* This inner class is only ever used for JSON deserialization. Please do not use in any other context.
12295
*/
123-
public static class Data {
96+
static class Data {
12497
/**
12598
* The problem type.
12699
*/
@@ -144,18 +117,18 @@ public static class Data {
144117
}
145118

146119
private static JsonService getJsonService() {
147-
if (ProblemDetails.isJsonServiceInitialized) {
148-
return ProblemDetails.jsonService;
120+
if (SolidProblemDetails.isJsonServiceInitialized) {
121+
return SolidProblemDetails.jsonService;
149122
}
150123
// It is a legitimate use case that this is loaded in a context where no implementation of the JSON service is
151124
// available. On SPI lookup failure, the ProblemDetails exceptions will fall back to default and not be parsed.
152125
try {
153-
ProblemDetails.jsonService = ServiceProvider.getJsonService();
126+
SolidProblemDetails.jsonService = ServiceProvider.getJsonService();
154127
} catch (IllegalStateException e) {
155-
ProblemDetails.jsonService = null;
128+
SolidProblemDetails.jsonService = null;
156129
}
157-
ProblemDetails.isJsonServiceInitialized = true;
158-
return ProblemDetails.jsonService;
130+
SolidProblemDetails.isJsonServiceInitialized = true;
131+
return SolidProblemDetails.jsonService;
159132
}
160133

161134
/**
@@ -165,15 +138,15 @@ private static JsonService getJsonService() {
165138
* @param body the HTTP error response body
166139
* @return a {@link ProblemDetails} instance
167140
*/
168-
public static ProblemDetails fromErrorResponse(
141+
public static SolidProblemDetails fromErrorResponse(
169142
final int statusCode,
170143
final Headers headers,
171144
final byte[] body
172145
) {
173146
final JsonService jsonService = getJsonService();
174147
if (jsonService == null
175148
|| (headers != null && !headers.allValues("Content-Type").contains(ProblemDetails.MIME_TYPE))) {
176-
return new ProblemDetails(
149+
return new SolidProblemDetails(
177150
URI.create(ProblemDetails.DEFAULT_TYPE),
178151
null,
179152
null,
@@ -190,15 +163,15 @@ public static ProblemDetails fromErrorResponse(
190163
.orElse(URI.create(ProblemDetails.DEFAULT_TYPE));
191164
// JSON mappers map invalid integers to 0, which is an invalid value in our case anyway.
192165
final int status = Optional.of(pdData.status).filter(s -> s != 0).orElse(statusCode);
193-
return new ProblemDetails(
166+
return new SolidProblemDetails(
194167
type,
195168
pdData.title,
196169
pdData.details,
197170
status,
198171
pdData.instance
199172
);
200173
} catch (IOException e) {
201-
return new ProblemDetails(
174+
return new SolidProblemDetails(
202175
URI.create(ProblemDetails.DEFAULT_TYPE),
203176
null,
204177
null,

solid/src/test/java/com/inrupt/client/solid/SolidClientTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ public int statusCode() {
492492
}
493493

494494
private static ProblemDetails mockProblemDetails(final String title, final String details, final int status) {
495-
return new ProblemDetails(URI.create("https://example.org/type"),
495+
return new SolidProblemDetails(URI.create("https://example.org/type"),
496496
title,
497497
details,
498498
status,

solid/src/test/java/com/inrupt/client/solid/ProblemDetailsTest.java renamed to solid/src/test/java/com/inrupt/client/solid/SolidProblemDetailsTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static org.junit.jupiter.api.Assertions.assertNull;
2525

2626
import com.inrupt.client.Headers;
27+
import com.inrupt.client.ProblemDetails;
2728

2829
import java.util.ArrayList;
2930
import java.util.HashMap;
@@ -35,7 +36,7 @@
3536

3637
// Ideally, this class should be in the api module, but it creates
3738
// a circular dependency with the JSON module implementation.
38-
class ProblemDetailsTest {
39+
class SolidProblemDetailsTest {
3940
Headers mockProblemDetailsHeader() {
4041
final List<String> headerValues = new ArrayList<>();
4142
headerValues.add("application/problem+json");
@@ -47,7 +48,7 @@ Headers mockProblemDetailsHeader() {
4748
@Test
4849
void testEmptyProblemDetails() {
4950
final int statusCode = 400;
50-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
51+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
5152
statusCode,
5253
mockProblemDetailsHeader(),
5354
"{}".getBytes()
@@ -61,7 +62,7 @@ void testEmptyProblemDetails() {
6162
@Test
6263
void testCompleteProblemDetails() {
6364
final int statusCode = 400;
64-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
65+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
6566
statusCode,
6667
mockProblemDetailsHeader(),
6768
("{" +
@@ -82,7 +83,7 @@ void testCompleteProblemDetails() {
8283
@Test
8384
void testIgnoreUnknownProblemDetails() {
8485
final int statusCode = 400;
85-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
86+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
8687
statusCode,
8788
mockProblemDetailsHeader(),
8889
("{" +
@@ -104,7 +105,7 @@ void testIgnoreUnknownProblemDetails() {
104105
@Test
105106
void testInvalidStatusProblemDetails() {
106107
final int statusCode = 400;
107-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
108+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
108109
statusCode,
109110
mockProblemDetailsHeader(),
110111
("{" +
@@ -117,7 +118,7 @@ void testInvalidStatusProblemDetails() {
117118
@Test
118119
void testMismatchingStatusProblemDetails() {
119120
final int statusCode = 400;
120-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
121+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
121122
statusCode,
122123
mockProblemDetailsHeader(),
123124
("{" +
@@ -129,7 +130,7 @@ void testMismatchingStatusProblemDetails() {
129130

130131
@Test
131132
void testInvalidTypeProblemDetails() {
132-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
133+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
133134
400,
134135
mockProblemDetailsHeader(),
135136
("{" +
@@ -141,7 +142,7 @@ void testInvalidTypeProblemDetails() {
141142

142143
@Test
143144
void testInvalidInstanceProblemDetails() {
144-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
145+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
145146
400,
146147
mockProblemDetailsHeader(),
147148
("{" +
@@ -154,7 +155,7 @@ void testInvalidInstanceProblemDetails() {
154155
@Test
155156
void testInvalidProblemDetails() {
156157
final int statusCode = 400;
157-
final ProblemDetails pd = ProblemDetails.fromErrorResponse(
158+
final ProblemDetails pd = SolidProblemDetails.fromErrorResponse(
158159
statusCode,
159160
mockProblemDetailsHeader(),
160161
"Not valid application/problem+json".getBytes()

0 commit comments

Comments
 (0)