From 4e1d173cc31cd8cb013c5e133b366aaa59350c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 1 May 2017 14:21:02 +0200 Subject: [PATCH] Add Problem object for JSON problem documents --- .../java/org/shredzone/acme4j/Problem.java | 69 +++++++++++++++++++ .../java/org/shredzone/acme4j/util/JSON.java | 14 ++++ .../org/shredzone/acme4j/ProblemTest.java | 41 +++++++++++ .../org/shredzone/acme4j/util/JSONTest.java | 16 ++++- .../src/test/resources/json/datatypes.json | 6 +- .../src/test/resources/json/problem.json | 4 ++ 6 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java create mode 100644 acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java create mode 100644 acme4j-client/src/test/resources/json/problem.json diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java new file mode 100644 index 00000000..66eb15cd --- /dev/null +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java @@ -0,0 +1,69 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 Richard "Shred" Körber + * http://acme4j.shredzone.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.shredzone.acme4j; + +import java.io.Serializable; + +import org.shredzone.acme4j.util.JSON; + +/** + * Represents a JSON Problem. + * + * @see RFC 7807 + */ +public class Problem implements Serializable { + private static final long serialVersionUID = -8418248862966754214L; + + private final JSON problem; + + /** + * Creates a new {@link Problem} object. + * + * @param problem + * Problem as JSON structure + */ + public Problem(JSON problem) { + this.problem = problem; + } + + /** + * Returns the problem type. + */ + public String getType() { + return problem.get("type").asString(); + } + + /** + * Returns a human-readable description of the problem. + */ + public String getDetail() { + return problem.get("detail").asString(); + } + + /** + * Returns the problem as {@link JSON} object, to access other fields. + */ + public JSON asJSON() { + return problem; + } + + /** + * Returns the problem as JSON string. + */ + @Override + public String toString() { + return problem.toString(); + } + +} diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java index 87d61620..9df71ade 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java @@ -41,6 +41,7 @@ import java.util.stream.StreamSupport; import org.jose4j.json.JsonUtil; import org.jose4j.lang.JoseException; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.Status; import org.shredzone.acme4j.exception.AcmeProtocolException; @@ -302,6 +303,19 @@ public final class JSON implements Serializable { } } + /** + * Returns the value as {@link Problem}. + * + * @return {@link Problem}, or {@code null} if the value was not set. + */ + public Problem asProblem() { + if (val == null) { + return null; + } + + return new Problem(asObject()); + } + /** * Returns the value as JSON array. * diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java new file mode 100644 index 00000000..edad3c9a --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java @@ -0,0 +1,41 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 Richard "Shred" Körber + * http://acme4j.shredzone.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.shredzone.acme4j; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import org.junit.Test; +import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link Problem}. + */ +public class ProblemTest { + + @Test + public void testProblem() { + JSON original = TestUtils.getJSON("problem"); + + Problem problem = new Problem(original); + + assertThat(problem.getType(), is("urn:ietf:params:acme:error:connection")); + assertThat(problem.getDetail(), is("connection refused")); + assertThat(problem.asJSON().toString(), is(sameJSONAs(original.toString()))); + assertThat(problem.toString(), is(sameJSONAs(original.toString()))); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java index 443c78bd..13838d62 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java @@ -36,6 +36,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; import org.junit.Test; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.Status; import org.shredzone.acme4j.exception.AcmeProtocolException; @@ -86,7 +87,7 @@ public class JSONTest { assertThat(json.keySet(), containsInAnyOrder( "text", "number", "boolean", "uri", "url", "date", "array", - "collect", "status", "binary")); + "collect", "status", "binary", "problem")); assertThat(json.contains("text"), is(true)); assertThat(json.contains("music"), is(false)); assertThat(json.get("text"), is(notNullValue())); @@ -208,6 +209,11 @@ public class JSONTest { JSON sub = array.get(3).asObject(); assertThat(sub.get("test").asString(), is("ok")); + + Problem problem = json.get("problem").asProblem(); + assertThat(problem, is(notNullValue())); + assertThat(problem.getType(), is("urn:ietf:params:acme:error:rateLimited")); + assertThat(problem.getDetail(), is("too many requests")); } /** @@ -225,6 +231,7 @@ public class JSONTest { 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(), is(nullValue())); try { json.get("none").asInt(); @@ -299,6 +306,13 @@ public class JSONTest { } catch (AcmeProtocolException ex) { // expected } + + try { + json.get("text").asProblem(); + fail("no exception was thrown"); + } catch (AcmeProtocolException ex) { + // expected + } } /** diff --git a/acme4j-client/src/test/resources/json/datatypes.json b/acme4j-client/src/test/resources/json/datatypes.json index 467becd2..72374e6d 100644 --- a/acme4j-client/src/test/resources/json/datatypes.json +++ b/acme4j-client/src/test/resources/json/datatypes.json @@ -8,5 +8,9 @@ "array": ["foo", 987, [1, 2, 3], {"test": "ok"}], "collect": ["foo", "bar", "barfoo"], "status": "VALID", - "binary": "Q2hhaW5zYXc" + "binary": "Q2hhaW5zYXc", + "problem": { + "type": "urn:ietf:params:acme:error:rateLimited", + "detail": "too many requests" + } } diff --git a/acme4j-client/src/test/resources/json/problem.json b/acme4j-client/src/test/resources/json/problem.json new file mode 100644 index 00000000..41e78281 --- /dev/null +++ b/acme4j-client/src/test/resources/json/problem.json @@ -0,0 +1,4 @@ +{ + "type": "urn:ietf:params:acme:error:connection", + "detail": "connection refused" +}