Skip to content

Commit 9212f20

Browse files
committed
#775 - Overhaul VndErrors to match spec.
See https://github.com/blongden/vnd.error for details on the vnd.error spec.
1 parent a597960 commit 9212f20

File tree

9 files changed

+336
-267
lines changed

9 files changed

+336
-267
lines changed

src/main/java/org/springframework/hateoas/MediaTypes.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,14 @@ public class MediaTypes {
7676
* Public constant media type for {@code application/vnd.collection+json}.
7777
*/
7878
public static final MediaType COLLECTION_JSON = MediaType.valueOf(COLLECTION_JSON_VALUE);
79+
80+
/**
81+
* A stringl equivalent of {@link MediaTypes#VND_ERROR_JSON}.
82+
*/
83+
public static final String VND_ERROR_JSON_VALUE = "application/vnd.error+json";
84+
85+
/**
86+
* Public constant media type for {@code application/vnd.error+json}.
87+
*/
88+
public static final MediaType VND_ERROR_JSON = MediaType.valueOf(VND_ERROR_JSON_VALUE);
7989
}

src/main/java/org/springframework/hateoas/VndErrors.java

Lines changed: 102 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -15,239 +15,181 @@
1515
*/
1616
package org.springframework.hateoas;
1717

18+
import lombok.EqualsAndHashCode;
19+
import lombok.Getter;
20+
import lombok.ToString;
21+
import lombok.experimental.Wither;
22+
1823
import java.util.ArrayList;
1924
import java.util.Arrays;
20-
import java.util.Iterator;
25+
import java.util.Collection;
2126
import java.util.List;
27+
import java.util.Objects;
2228

29+
import org.springframework.hateoas.core.Relation;
2330
import org.springframework.util.Assert;
2431
import org.springframework.util.StringUtils;
2532

2633
import com.fasterxml.jackson.annotation.JsonCreator;
34+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
35+
import com.fasterxml.jackson.annotation.JsonInclude;
2736
import com.fasterxml.jackson.annotation.JsonProperty;
28-
import com.fasterxml.jackson.annotation.JsonValue;
37+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
2938

3039
/**
31-
* A representation model class to be rendered as specified for the media type {@code application/vnd.error}.
40+
* A representation model class to be rendered as specified for the media type {@code application/vnd.error+json}.
3241
*
3342
* @see https://github.com/blongden/vnd.error
3443
* @author Oliver Gierke
3544
* @author Greg Turnquist
3645
*/
37-
public class VndErrors implements Iterable<VndErrors.VndError> {
46+
@JsonPropertyOrder({"message", "logref", "total", "_links", "_embedded"})
47+
@JsonIgnoreProperties(ignoreUnknown = true)
48+
@EqualsAndHashCode
49+
@ToString
50+
public class VndErrors extends Resources<VndErrors.VndError> {
3851

39-
private final List<VndError> vndErrors;
52+
public static final String REL_HELP = "help";
53+
public static final String REL_DESCRIBES = "describes";
54+
public static final String REL_ABOUT = "about";
4055

41-
/**
42-
* Creates a new {@link VndErrors} instance containing a single {@link VndError} with the given logref, message and
43-
* optional {@link Link}s.
44-
*
45-
* @param logref must not be {@literal null} or empty.
46-
* @param message must not be {@literal null} or empty.
47-
* @param links
48-
*/
49-
public VndErrors(String logref, String message, Link... links) {
50-
this(new VndError(logref, message, links));
51-
}
56+
private final List<VndError> errors;
5257

53-
/**
54-
* Creates a new {@link VndErrors} wrapper for at least one {@link VndError}.
55-
*
56-
* @param errors must not be {@literal null}.
57-
* @param errors
58-
*/
59-
public VndErrors(VndError error, VndError... errors) {
58+
@Getter
59+
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
60+
private final String message;
6061

61-
Assert.notNull(error, "Error must not be null");
62-
63-
this.vndErrors = new ArrayList<VndError>(errors.length + 1);
64-
this.vndErrors.add(error);
65-
this.vndErrors.addAll(Arrays.asList(errors));
66-
}
62+
@Getter
63+
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
64+
private final Integer logref;
6765

6866
/**
6967
* Creates a new {@link VndErrors} wrapper for the given {@link VndErrors}.
7068
*
7169
* @param errors must not be {@literal null} or empty.
7270
*/
7371
@JsonCreator
74-
public VndErrors(List<VndError> errors) {
72+
public VndErrors(@JsonProperty("_embedded") List<VndError> errors, @JsonProperty("message") String message,
73+
@JsonProperty("logref") Integer logref, @JsonProperty("_links") List<Link> links) {
7574

7675
Assert.notNull(errors, "Errors must not be null!");
77-
Assert.isTrue(!errors.isEmpty(), "Errors must not be empty!");
78-
this.vndErrors = errors;
76+
Assert.notEmpty(errors, "Errors must not be empty!");
77+
78+
this.errors = errors;
79+
this.message = message;
80+
this.logref = logref;
81+
if (links != null && !links.isEmpty()) {
82+
add(links);
83+
}
7984
}
8085

81-
/**
82-
* Protected default constructor to allow JAXB marshalling.
83-
*/
84-
protected VndErrors() {
85-
this.vndErrors = new ArrayList<VndError>();
86+
public VndErrors() {
87+
88+
this.errors = new ArrayList<>();
89+
this.message = null;
90+
this.logref = null;
8691
}
8792

88-
/**
89-
* Adds an additional {@link VndError} to the wrapper.
90-
*
91-
* @param error
92-
*/
93-
public VndErrors add(VndError error) {
94-
this.vndErrors.add(error);
95-
return this;
93+
public VndErrors withMessage(String message) {
94+
return new VndErrors(this.errors, message, this.logref, this.getLinks());
9695
}
9796

98-
/**
99-
* Dummy method to allow {@link JsonValue} to be configured.
100-
*
101-
* @return the vndErrors
102-
*/
103-
@JsonValue
104-
private List<VndError> getErrors() {
105-
return vndErrors;
97+
public VndErrors withLogref(Integer logref) {
98+
return new VndErrors(this.errors, this.message, logref, this.getLinks());
10699
}
107100

108-
/*
109-
* (non-Javadoc)
110-
* @see java.lang.Iterable#iterator()
111-
*/
112-
@Override
113-
public Iterator<VndErrors.VndError> iterator() {
114-
return this.vndErrors.iterator();
101+
public VndErrors withErrors(List<VndError> errors) {
102+
103+
Assert.notNull(errors, "errors must not be null!");
104+
Assert.notEmpty(errors, "errors must not empty!");
105+
106+
return new VndErrors(errors, this.message, this.logref, this.getLinks());
115107
}
116108

117-
/*
118-
* (non-Javadoc)
119-
* @see java.lang.Object#toString()
120-
*/
121-
@Override
122-
public String toString() {
123-
return String.format("VndErrors[%s]", StringUtils.collectionToCommaDelimitedString(vndErrors));
109+
public VndErrors withError(VndError error) {
110+
111+
this.errors.add(error);
112+
return new VndErrors(this.errors, this.message, this.logref, this.getLinks());
124113
}
125114

126-
/*
127-
* (non-Javadoc)
128-
* @see java.lang.Object#hashCode()
129-
*/
130-
@Override
131-
public int hashCode() {
132-
return vndErrors.hashCode();
115+
public VndErrors withLink(Link link) {
116+
117+
add(link);
118+
return new VndErrors(this.errors, this.message, this.logref, this.getLinks());
133119
}
134120

135-
/*
136-
* (non-Javadoc)
137-
* @see java.lang.Object#equals(java.lang.Object)
121+
/**
122+
* Returns the underlying elements.
123+
*
124+
* @return the content will never be {@literal null}.
138125
*/
139126
@Override
140-
public boolean equals(Object obj) {
141-
142-
if (this == obj) {
143-
return true;
144-
}
145-
146-
if (!(obj instanceof VndErrors)) {
147-
return false;
148-
}
127+
public Collection<VndError> getContent() {
128+
return this.errors;
129+
}
149130

150-
VndErrors that = (VndErrors) obj;
151-
return this.vndErrors.equals(that.vndErrors);
131+
/**
132+
* Virtual attribute to generate JSON field of {@literal total}.
133+
*/
134+
public int getTotal() {
135+
return this.errors.size();
152136
}
153137

154138
/**
155139
* A single {@link VndError}.
156140
*
157141
* @author Oliver Gierke
142+
* @author Greg Turnquist
158143
*/
144+
@JsonPropertyOrder({"message", "path", "logref"})
145+
@Relation(collectionRelation = "errors")
146+
@EqualsAndHashCode
159147
public static class VndError extends ResourceSupport {
160148

161-
@JsonProperty private final String logref;
162-
@JsonProperty private final String message;
149+
@Getter
150+
private final String message;
151+
152+
@Getter
153+
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
154+
private final String path;
155+
156+
@Getter
157+
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)
158+
private final Integer logref;
163159

164160
/**
165-
* Creates a new {@link VndError} with the given logref, a message as well as some {@link Link}s.
161+
* Creates a new {@link VndError} with a message and optional a path and a logref.
166162
*
167-
* @param logref must not be {@literal null} or empty.
168163
* @param message must not be {@literal null} or empty.
169-
* @param links
170164
*/
171-
public VndError(String logref, String message, Link... links) {
165+
@JsonCreator
166+
public VndError(@JsonProperty("message") String message, @JsonProperty("path") String path,
167+
@JsonProperty("logref") Integer logref, @JsonProperty("_links") List<Link> links) {
172168

173-
Assert.hasText(logref, "Logref must not be null or empty!");
174169
Assert.hasText(message, "Message must not be null or empty!");
175170

176-
this.logref = logref;
177171
this.message = message;
178-
this.add(Arrays.asList(links));
179-
}
180-
181-
/**
182-
* Protected default constructor to allow JAXB marshalling.
183-
*/
184-
protected VndError() {
185-
186-
this.logref = null;
187-
this.message = null;
188-
}
172+
this.path = path;
173+
this.logref = logref;
189174

190-
/**
191-
* Returns the logref of the error.
192-
*
193-
* @return the logref
194-
*/
195-
public String getLogref() {
196-
return logref;
175+
this.add(links);
197176
}
198177

199178
/**
200-
* Returns the message of the error.
201-
*
202-
* @return the message
179+
* Convenience constructor
203180
*/
204-
public String getMessage() {
205-
return message;
181+
public VndError(String message, String path, Integer logref, Link... links) {
182+
this(message, path, logref, Arrays.asList(links));
206183
}
207184

208-
/*
209-
* (non-Javadoc)
210-
* @see org.springframework.hateoas.ResourceSupport#toString()
211-
*/
212185
@Override
213186
public String toString() {
214-
return String.format("VndError[logref: %s, message: %s, links: [%s]]", logref, message,
215-
StringUtils.collectionToCommaDelimitedString(getLinks()));
216-
}
217-
218-
/*
219-
* (non-Javadoc)
220-
* @see org.springframework.hateoas.ResourceSupport#hashCode()
221-
*/
222-
@Override
223-
public int hashCode() {
224-
225-
int result = 17;
226-
227-
result += 31 * logref.hashCode();
228-
result += 31 * message.hashCode();
229-
230-
return result;
231-
}
232-
233-
/*
234-
* (non-Javadoc)
235-
* @see org.springframework.hateoas.ResourceSupport#equals(java.lang.Object)
236-
*/
237-
@Override
238-
public boolean equals(Object obj) {
239-
240-
if (obj == this) {
241-
return true;
242-
}
243-
244-
if (!(obj instanceof VndError)) {
245-
return false;
246-
}
247-
248-
VndError that = (VndError) obj;
249-
250-
return this.logref.equals(that.logref) && this.message.equals(that.message);
187+
return "VndError{" +
188+
"message='" + message + '\'' +
189+
", path='" + path + '\'' +
190+
", logref=" + logref +
191+
", links=" + getLinks() +
192+
'}';
251193
}
252194
}
253195
}

0 commit comments

Comments
 (0)