JSON getters never return null

pull/61/head
Richard Körber 2018-03-17 18:18:44 +01:00
parent 4b3eb22eef
commit 4de82be5f3
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
13 changed files with 179 additions and 154 deletions

View File

@ -64,7 +64,7 @@ public class Account extends AcmeJsonResource {
* {@code null} if the server did not provide such an information. * {@code null} if the server did not provide such an information.
*/ */
public Boolean getTermsOfServiceAgreed() { public Boolean getTermsOfServiceAgreed() {
return getJSON().get(KEY_TOS_AGREED).optional().map(Value::asBoolean).orElse(null); return getJSON().get(KEY_TOS_AGREED).map(Value::asBoolean).orElse(null);
} }
/** /**
@ -85,7 +85,7 @@ public class Account extends AcmeJsonResource {
* {@link Status#REVOKED}. * {@link Status#REVOKED}.
*/ */
public Status getStatus() { public Status getStatus() {
return getJSON().get(KEY_STATUS).asStatusOrElse(Status.UNKNOWN); return getJSON().get(KEY_STATUS).asStatus();
} }
/** /**

View File

@ -50,12 +50,12 @@ public class Authorization extends AcmeJsonResource {
* order, check the {@link #isWildcard()} method. * order, check the {@link #isWildcard()} method.
*/ */
public String getDomain() { public String getDomain() {
JSON jsonIdentifier = getJSON().get("identifier").required().asObject(); JSON jsonIdentifier = getJSON().get("identifier").asObject();
String type = jsonIdentifier.get("type").required().asString(); String type = jsonIdentifier.get("type").asString();
if (!"dns".equals(type)) { if (!"dns".equals(type)) {
throw new AcmeProtocolException("Unknown authorization type: " + type); throw new AcmeProtocolException("Unknown authorization type: " + type);
} }
return jsonIdentifier.get("value").required().asString(); return jsonIdentifier.get("value").asString();
} }
/** /**
@ -65,14 +65,14 @@ public class Authorization extends AcmeJsonResource {
* {@link Status#INVALID}, {@link Status#DEACTIVATED}, {@link Status#REVOKED}. * {@link Status#INVALID}, {@link Status#DEACTIVATED}, {@link Status#REVOKED}.
*/ */
public Status getStatus() { public Status getStatus() {
return getJSON().get("status").asStatusOrElse(Status.UNKNOWN); return getJSON().get("status").asStatus();
} }
/** /**
* Gets the expiry date of the authorization, if set by the server. * Gets the expiry date of the authorization, if set by the server.
*/ */
public Instant getExpires() { public Instant getExpires() {
return getJSON().get("expires").optional() return getJSON().get("expires")
.map(Value::asString) .map(Value::asString)
.map(AcmeUtils::parseTimestamp) .map(AcmeUtils::parseTimestamp)
.orElse(null); .orElse(null);
@ -83,7 +83,7 @@ public class Authorization extends AcmeJsonResource {
* {@code false} otherwise. * {@code false} otherwise.
*/ */
public boolean isWildcard() { public boolean isWildcard() {
return getJSON().get("wildcard").optional() return getJSON().get("wildcard")
.map(Value::asBoolean) .map(Value::asBoolean)
.orElse(false); .orElse(false);
} }

View File

@ -44,7 +44,7 @@ public class Metadata {
* available. * available.
*/ */
public URI getTermsOfService() { public URI getTermsOfService() {
return meta.get("termsOfService").asURI(); return meta.get("termsOfService").map(Value::asURI).orElse(null);
} }
/** /**
@ -52,7 +52,7 @@ public class Metadata {
* server. {@code null} if not available. * server. {@code null} if not available.
*/ */
public URL getWebsite() { public URL getWebsite() {
return meta.get("website").asURL(); return meta.get("website").map(Value::asURL).orElse(null);
} }
/** /**
@ -60,7 +60,9 @@ public class Metadata {
* itself for the purposes of CAA record validation. Empty if not available. * itself for the purposes of CAA record validation. Empty if not available.
*/ */
public Collection<String> getCaaIdentities() { public Collection<String> getCaaIdentities() {
return meta.get("caaIdentities").asArray().stream() return meta.get("caaIdentities")
.asArray()
.stream()
.map(Value::asString) .map(Value::asString)
.collect(toList()); .collect(toList());
} }
@ -69,7 +71,7 @@ public class Metadata {
* Returns whether an external account is required by this CA. * Returns whether an external account is required by this CA.
*/ */
public boolean isExternalAccountRequired() { public boolean isExternalAccountRequired() {
return meta.get("externalAccountRequired").orElse(false).asBoolean(); return meta.get("externalAccountRequired").map(Value::asBoolean).orElse(false);
} }
/** /**

View File

@ -45,21 +45,21 @@ public class Order extends AcmeJsonResource {
* {@link Status#PROCESSING}, {@link Status#VALID}, {@link Status#INVALID}. * {@link Status#PROCESSING}, {@link Status#VALID}, {@link Status#INVALID}.
*/ */
public Status getStatus() { public Status getStatus() {
return getJSON().get("status").asStatusOrElse(Status.UNKNOWN); return getJSON().get("status").asStatus();
} }
/** /**
* Returns a {@link Problem} document if the order failed. * Returns a {@link Problem} document if the order failed.
*/ */
public Problem getError() { public Problem getError() {
return getJSON().get("error").asProblem(getLocation()); return getJSON().get("error").map(v -> v.asProblem(getLocation())).orElse(null);
} }
/** /**
* Gets the expiry date of the authorization, if set by the server. * Gets the expiry date of the authorization, if set by the server.
*/ */
public Instant getExpires() { public Instant getExpires() {
return getJSON().get("expires").asInstant(); return getJSON().get("expires").map(Value::asInstant).orElse(null);
} }
/** /**
@ -78,14 +78,14 @@ public class Order extends AcmeJsonResource {
* Gets the "not before" date that was used for the order, or {@code null}. * Gets the "not before" date that was used for the order, or {@code null}.
*/ */
public Instant getNotBefore() { public Instant getNotBefore() {
return getJSON().get("notBefore").asInstant(); return getJSON().get("notBefore").map(Value::asInstant).orElse(null);
} }
/** /**
* Gets the "not after" date that was used for the order, or {@code null}. * Gets the "not after" date that was used for the order, or {@code null}.
*/ */
public Instant getNotAfter() { public Instant getNotAfter() {
return getJSON().get("notAfter").asInstant(); return getJSON().get("notAfter").map(Value::asInstant).orElse(null);
} }
/** /**
@ -114,7 +114,7 @@ public class Order extends AcmeJsonResource {
* Gets the {@link Certificate} if it is available. {@code null} otherwise. * Gets the {@link Certificate} if it is available. {@code null} otherwise.
*/ */
public Certificate getCertificate() { public Certificate getCertificate() {
return getJSON().get("certificate").optional() return getJSON().get("certificate")
.map(Value::asURL) .map(Value::asURL)
.map(getLogin()::bindCertificate) .map(getLogin()::bindCertificate)
.orElse(null); .orElse(null);

View File

@ -24,6 +24,7 @@ import java.util.List;
import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON; import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
/** /**
* Represents a JSON Problem. * Represents a JSON Problem.
@ -53,12 +54,16 @@ public class Problem implements Serializable {
* Returns the problem type. It is always an absolute URI. * Returns the problem type. It is always an absolute URI.
*/ */
public URI getType() { public URI getType() {
return problemJson.get("type")
.map(Value::asString)
.map(it -> {
try { try {
String type = problemJson.get("type").asString(); return baseUrl.toURI().resolve(it);
return type != null ? baseUrl.toURI().resolve(type) : null;
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
throw new IllegalArgumentException("Bad base URL", ex); throw new IllegalArgumentException("Bad base URL", ex);
} }
})
.orElse(null);
} }
/** /**
@ -68,7 +73,7 @@ public class Problem implements Serializable {
* @see #toString() * @see #toString()
*/ */
public String getTitle() { public String getTitle() {
return problemJson.get("title").asString(); return problemJson.get("title").map(Value::asString).orElse(null);
} }
/** /**
@ -78,7 +83,7 @@ public class Problem implements Serializable {
* @see #toString() * @see #toString()
*/ */
public String getDetail() { public String getDetail() {
return problemJson.get("detail").asString(); return problemJson.get("detail").map(Value::asString).orElse(null);
} }
/** /**
@ -86,29 +91,35 @@ public class Problem implements Serializable {
* an absolute URI. * an absolute URI.
*/ */
public URI getInstance() { public URI getInstance() {
return problemJson.get("instance")
.map(Value::asString)
.map(it -> {
try { try {
String instance = problemJson.get("instance").asString(); return baseUrl.toURI().resolve(it);
return instance != null ? baseUrl.toURI().resolve(instance) : null;
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
throw new IllegalArgumentException("Bad base URL", ex); throw new IllegalArgumentException("Bad base URL", ex);
} }
})
.orElse(null);
} }
/** /**
* Returns the domain this problem relates to. May be {@code null}. * Returns the domain this problem relates to. May be {@code null}.
*/ */
public String getDomain() { public String getDomain() {
JSON identifier = problemJson.get("identifier").asObject(); Value identifier = problemJson.get("identifier");
if (identifier == null) { if (!identifier.isPresent()) {
return null; return null;
} }
String type = identifier.get("type").asString(); JSON json = identifier.asObject();
String type = json.get("type").asString();
if (!"dns".equals(type)) { if (!"dns".equals(type)) {
throw new AcmeProtocolException("Cannot process a " + type + " identifier"); throw new AcmeProtocolException("Cannot process a " + type + " identifier");
} }
return identifier.get("value").asString(); return json.get("value").asString();
} }
/** /**
@ -117,7 +128,8 @@ public class Problem implements Serializable {
public List<Problem> getSubProblems() { public List<Problem> getSubProblems() {
return unmodifiableList( return unmodifiableList(
problemJson.get("subproblems") problemJson.get("subproblems")
.asArray().stream() .asArray()
.stream()
.map(o -> o.asProblem(baseUrl)) .map(o -> o.asProblem(baseUrl))
.collect(toList()) .collect(toList())
); );

View File

@ -31,6 +31,7 @@ import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.provider.AcmeProvider; import org.shredzone.acme4j.provider.AcmeProvider;
import org.shredzone.acme4j.toolbox.JSON; import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
/** /**
* A session stores the ACME server URI. It also tracks communication parameters. * A session stores the ACME server URI. It also tracks communication parameters.
@ -193,19 +194,18 @@ public class Session {
JSON directoryJson = provider().directory(this, getServerUri()); JSON directoryJson = provider().directory(this, getServerUri());
JSON meta = directoryJson.get("meta").asObject(); Value meta = directoryJson.get("meta");
if (meta != null) { if (meta.isPresent()) {
metadata.set(new Metadata(meta)); metadata.set(new Metadata(meta.asObject()));
} else { } else {
metadata.set(new Metadata(JSON.empty())); metadata.set(new Metadata(JSON.empty()));
} }
Map<Resource, URL> map = new EnumMap<>(Resource.class); Map<Resource, URL> map = new EnumMap<>(Resource.class);
for (Resource res : Resource.values()) { for (Resource res : Resource.values()) {
URL url = directoryJson.get(res.path()).asURL(); directoryJson.get(res.path())
if (url != null) { .map(Value::asURL)
map.put(res, url); .ifPresent(url -> map.put(res, url));
}
} }
resourceMap.set(map); resourceMap.set(map);

View File

@ -23,6 +23,7 @@ import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON; import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.shredzone.acme4j.toolbox.JSONBuilder; import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -55,7 +56,7 @@ public class Challenge extends AcmeJsonResource {
* {@link JSON} challenge data * {@link JSON} challenge data
*/ */
public Challenge(Login login, JSON data) { public Challenge(Login login, JSON data) {
super(login, data.get(KEY_URL).required().asURL()); super(login, data.get(KEY_URL).asURL());
setJSON(data); setJSON(data);
} }
@ -73,14 +74,14 @@ public class Challenge extends AcmeJsonResource {
* {@link Status#VALID}, {@link Status#INVALID}. * {@link Status#VALID}, {@link Status#INVALID}.
*/ */
public Status getStatus() { public Status getStatus() {
return getJSON().get(KEY_STATUS).asStatusOrElse(Status.UNKNOWN); return getJSON().get(KEY_STATUS).asStatus();
} }
/** /**
* Returns the validation date, if returned by the server. * Returns the validation date, if returned by the server.
*/ */
public Instant getValidated() { public Instant getValidated() {
return getJSON().get(KEY_VALIDATED).asInstant(); return getJSON().get(KEY_VALIDATED).map(Value::asInstant).orElse(null);
} }
/** /**
@ -89,7 +90,9 @@ public class Challenge extends AcmeJsonResource {
* {@link Problem#getSubProblems()}. * {@link Problem#getSubProblems()}.
*/ */
public Problem getError() { public Problem getError() {
return getJSON().get(KEY_ERROR).asProblem(getLocation()); return getJSON().get(KEY_ERROR)
.map(it -> it.asProblem(getLocation()))
.orElse(null);
} }
/** /**
@ -115,13 +118,13 @@ public class Challenge extends AcmeJsonResource {
@Override @Override
protected void setJSON(JSON json) { protected void setJSON(JSON json) {
String type = json.get(KEY_TYPE).required().asString(); String type = json.get(KEY_TYPE).asString();
if (!acceptable(type)) { if (!acceptable(type)) {
throw new AcmeProtocolException("incompatible type " + type + " for this challenge"); throw new AcmeProtocolException("incompatible type " + type + " for this challenge");
} }
String loc = json.get(KEY_URL).required().asString(); String loc = json.get(KEY_URL).asString();
if (loc != null && !loc.equals(getLocation().toString())) { if (loc != null && !loc.equals(getLocation().toString())) {
throw new AcmeProtocolException("challenge has changed its location"); throw new AcmeProtocolException("challenge has changed its location");
} }

View File

@ -56,7 +56,7 @@ public class TokenChallenge extends Challenge {
* Gets the token. * Gets the token.
*/ */
protected String getToken() { protected String getToken() {
return getJSON().get(KEY_TOKEN).required().asString(); return getJSON().get(KEY_TOKEN).asString();
} }
/** /**

View File

@ -86,7 +86,7 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
Objects.requireNonNull(login, "login"); Objects.requireNonNull(login, "login");
Objects.requireNonNull(data, "data"); Objects.requireNonNull(data, "data");
String type = data.get("type").required().asString(); String type = data.get("type").asString();
BiFunction<Login, JSON, Challenge> constructor = CHALLENGES.get(type); BiFunction<Login, JSON, Challenge> constructor = CHALLENGES.get(type);
if (constructor != null) { if (constructor != null) {

View File

@ -37,6 +37,7 @@ import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@ -247,6 +248,9 @@ public final class JSON implements Serializable {
/** /**
* A single JSON value. This instance also covers {@code null} values. * A single JSON value. This instance also covers {@code null} values.
* <p>
* All return values are never {@code null} unless specified otherwise. For optional
* parameters, use {@link Value#optional()}.
*/ */
public static final class Value { public static final class Value {
private final String path; private final String path;
@ -265,58 +269,53 @@ public final class JSON implements Serializable {
this.val = val; this.val = val;
} }
/**
* Checks if this value is {@code null}.
*
* @return {@code true} if this value is present, {@code false} if {@code null}.
*/
public boolean isPresent() {
return val != null;
}
/** /**
* Returns this value as {@link Optional}, for further mapping and filtering. * Returns this value as {@link Optional}, for further mapping and filtering.
* *
* @return {@link Optional} of this value, or {@link Optional#empty()} if this * @return {@link Optional} of this value, or {@link Optional#empty()} if this
* value is {@code null}. * value is {@code null}.
* @see #map(Function)
*/ */
public Optional<Value> optional() { public Optional<Value> optional() {
return val != null ? Optional.of(this) : Optional.empty(); return val != null ? Optional.of(this) : Optional.empty();
} }
/** /**
* Checks if the value is present. An {@link AcmeProtocolException} is thrown if * Returns this value as an {@link Optional} of the desired type, for further
* the value is {@code null}. * mapping and filtering.
* *
* @return itself * @param mapper
* A {@link Function} that converts a {@link Value} to the desired type
* @return {@link Optional} of this value, or {@link Optional#empty()} if this
* value is {@code null}.
* @see #optional()
*/ */
public Value required() { public <T> Optional<T> map(Function <Value, T> mapper) {
if (val == null) { return optional().map(mapper);
throw new AcmeProtocolException(path + ": required, but not set");
}
return this;
}
/**
* Checks if the value is present. If not, the default value is used instead.
*
* @param def Default value
* @return itself
*/
public Value orElse(Object def) {
return val != null ? this : new Value(path, def);
} }
/** /**
* Returns the value as {@link String}. * Returns the value as {@link String}.
*
* @return {@link String}, or {@code null} if the value was not set.
*/ */
public String asString() { public String asString() {
return val != null ? val.toString() : null; required();
return val.toString();
} }
/** /**
* Returns the value as JSON object. * Returns the value as JSON object.
*
* @return {@link JSON}, or {@code null} if the value was not set.
*/ */
public JSON asObject() { public JSON asObject() {
if (val == null) { required();
return null;
}
try { try {
return new JSON(path, (Map<String, Object>) val); return new JSON(path, (Map<String, Object>) val);
} catch (ClassCastException ex) { } catch (ClassCastException ex) {
@ -329,20 +328,17 @@ public final class JSON implements Serializable {
* *
* @param baseUrl * @param baseUrl
* Base {@link URL} to resolve relative links against * Base {@link URL} to resolve relative links against
* @return {@link Problem}, or {@code null} if the value was not set.
*/ */
public Problem asProblem(URL baseUrl) { public Problem asProblem(URL baseUrl) {
if (val == null) { required();
return null;
}
return new Problem(asObject(), baseUrl); return new Problem(asObject(), baseUrl);
} }
/** /**
* Returns the value as JSON array. * Returns the value as {@link JSON.Array}.
* * <p>
* @return {@link JSON.Array}, which is empty if the value was not set. * Unlike the other getters, this method returns an empty array if the value is
* not set. Use {@link #isPresent()} to find out if the value was actually set.
*/ */
public Array asArray() { public Array asArray() {
if (val == null) { if (val == null) {
@ -358,12 +354,9 @@ public final class JSON implements Serializable {
/** /**
* Returns the value as int. * Returns the value as int.
*
* @return integer value
*/ */
public int asInt() { public int asInt() {
required(); required();
try { try {
return ((Number) val).intValue(); return ((Number) val).intValue();
} catch (ClassCastException ex) { } catch (ClassCastException ex) {
@ -373,12 +366,9 @@ public final class JSON implements Serializable {
/** /**
* Returns the value as boolean. * Returns the value as boolean.
*
* @return integer value
*/ */
public boolean asBoolean() { public boolean asBoolean() {
required(); required();
try { try {
return (Boolean) val; return (Boolean) val;
} catch (ClassCastException ex) { } catch (ClassCastException ex) {
@ -388,14 +378,9 @@ public final class JSON implements Serializable {
/** /**
* Returns the value as {@link URI}. * Returns the value as {@link URI}.
*
* @return {@link URI}, or {@code null} if the value was not set.
*/ */
public URI asURI() { public URI asURI() {
if (val == null) { required();
return null;
}
try { try {
return new URI(val.toString()); return new URI(val.toString());
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
@ -405,14 +390,9 @@ public final class JSON implements Serializable {
/** /**
* Returns the value as {@link URL}. * Returns the value as {@link URL}.
*
* @return {@link URL}, or {@code null} if the value was not set.
*/ */
public URL asURL() { public URL asURL() {
if (val == null) { required();
return null;
}
try { try {
return new URL(val.toString()); return new URL(val.toString());
} catch (MalformedURLException ex) { } catch (MalformedURLException ex) {
@ -422,14 +402,9 @@ public final class JSON implements Serializable {
/** /**
* Returns the value as {@link Instant}. * Returns the value as {@link Instant}.
*
* @return {@link Instant}, or {@code null} if the value was not set.
*/ */
public Instant asInstant() { public Instant asInstant() {
if (val == null) { required();
return null;
}
try { try {
return parseTimestamp(val.toString()); return parseTimestamp(val.toString());
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
@ -439,30 +414,28 @@ public final class JSON implements Serializable {
/** /**
* Returns the value as base64 decoded byte array. * Returns the value as base64 decoded byte array.
*
* @return byte array, or {@code null} if the value was not set.
*/ */
public byte[] asBinary() { public byte[] asBinary() {
if (val == null) { required();
return null; //NOSONAR: we want to return null here
}
return AcmeUtils.base64UrlDecode(val.toString()); return AcmeUtils.base64UrlDecode(val.toString());
} }
/** /**
* Returns the parsed status. * Returns the parsed {@link Status}.
*
* @param def
* Default status if value is not present or {@code null}
* @return {@link Status}
*/ */
public Status asStatusOrElse(Status def) { public Status asStatus() {
if (val == null) { required();
return def; return Status.parse(val.toString());
} }
return Status.parse(val.toString()); /**
* Checks if the value is present. An {@link AcmeProtocolException} is thrown if
* the value is {@code null}.
*/
private void required() {
if (!isPresent()) {
throw new AcmeProtocolException(path + ": required, but not set");
}
} }
@Override @Override

View File

@ -121,7 +121,6 @@ public class AccountBuilderTest {
JSON binding = claims.toJSON() JSON binding = claims.toJSON()
.get("externalAccountBinding") .get("externalAccountBinding")
.required()
.asObject(); .asObject();
String encodedHeader = binding.get("protected").asString(); String encodedHeader = binding.get("protected").asString();

View File

@ -60,8 +60,6 @@ public class ChallengeTest {
assertThat(challenge.getValidated(), is(parseTimestamp("2015-12-12T17:19:36.336785823Z"))); assertThat(challenge.getValidated(), is(parseTimestamp("2015-12-12T17:19:36.336785823Z")));
assertThat(challenge.getJSON().get("type").asString(), is("generic-01")); assertThat(challenge.getJSON().get("type").asString(), is("generic-01"));
assertThat(challenge.getJSON().get("url").asURL(), is(url("http://example.com/challenge/123"))); assertThat(challenge.getJSON().get("url").asURL(), is(url("http://example.com/challenge/123")));
assertThat(challenge.getJSON().get("notPresent").asString(), is(nullValue()));
assertThat(challenge.getJSON().get("notPresentUrl").asURL(), is(nullValue()));
Problem error = challenge.getError(); Problem error = challenge.getError();
assertThat(error, is(notNullValue())); assertThat(error, is(notNullValue()));

View File

@ -40,6 +40,7 @@ import org.junit.Test;
import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.Problem;
import org.shredzone.acme4j.Status; import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON.Value;
/** /**
* Unit test for {@link JSON}. * Unit test for {@link JSON}.
@ -198,10 +199,12 @@ public class JSONTest {
assertThat(json.get("uri").asURI(), is(URI.create("mailto:foo@example.com"))); assertThat(json.get("uri").asURI(), is(URI.create("mailto:foo@example.com")));
assertThat(json.get("url").asURL(), is(url("http://example.com"))); assertThat(json.get("url").asURL(), is(url("http://example.com")));
assertThat(json.get("date").asInstant(), is(date)); assertThat(json.get("date").asInstant(), is(date));
assertThat(json.get("status").asStatusOrElse(Status.INVALID), is(Status.VALID)); assertThat(json.get("status").asStatus(), is(Status.VALID));
assertThat(json.get("binary").asBinary(), is("Chainsaw".getBytes())); assertThat(json.get("binary").asBinary(), is("Chainsaw".getBytes()));
assertThat(json.get("text").isPresent(), is(true));
assertThat(json.get("text").optional().isPresent(), is(true)); assertThat(json.get("text").optional().isPresent(), is(true));
assertThat(json.get("text").map(Value::asString).isPresent(), is(true));
JSON.Array array = json.get("array").asArray(); JSON.Array array = json.get("array").asArray();
assertThat(array.get(0).asString(), is("foo")); assertThat(array.get(0).asString(), is("foo"));
@ -230,20 +233,65 @@ public class JSONTest {
JSON json = TestUtils.getJSON("datatypes"); JSON json = TestUtils.getJSON("datatypes");
assertThat(json.get("none"), is(notNullValue())); assertThat(json.get("none"), is(notNullValue()));
assertThat(json.get("none").asString(), is(nullValue())); assertThat(json.get("none").isPresent(), is(false));
assertThat(json.get("none").asURI(), is(nullValue()));
assertThat(json.get("none").asURL(), is(nullValue()));
assertThat(json.get("none").asInstant(), is(nullValue()));
assertThat(json.get("none").asObject(), is(nullValue()));
assertThat(json.get("none").asStatusOrElse(Status.INVALID), is(Status.INVALID));
assertThat(json.get("none").asBinary(), is(nullValue()));
assertThat(json.get("none").asProblem(BASE_URL), is(nullValue()));
assertThat(json.get("none").orElse("foo").asString(), is("foo"));
assertThat(json.get("none").orElse(42).asInt(), is(42));
assertThat(json.get("none").orElse(true).asBoolean(), is(true));
assertThat(json.get("none").optional().isPresent(), is(false)); assertThat(json.get("none").optional().isPresent(), is(false));
assertThat(json.get("none").map(Value::asString).isPresent(), is(false));
try {
json.get("none").asString();
fail("asString did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asURI();
fail("asURI did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asURL();
fail("asURL did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asInstant();
fail("asInstant did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asObject();
fail("asObject did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asStatus();
fail("asStatus did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asBinary();
fail("asBinary did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try {
json.get("none").asProblem(BASE_URL);
fail("asProblem did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
try { try {
json.get("none").asInt(); json.get("none").asInt();
@ -258,16 +306,6 @@ public class JSONTest {
} catch (AcmeProtocolException ex) { } catch (AcmeProtocolException ex) {
// expected // expected
} }
try {
json.get("none").required();
fail("required did not fail");
} catch (AcmeProtocolException ex) {
// expected
}
JSON.Value textv = json.get("text");
assertThat(textv.required(), is(textv));
} }
/** /**