diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadGatewayProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadGatewayProblem.java index 6cb8aab8..9ebf01eb 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadGatewayProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadGatewayProblem.java @@ -2,6 +2,7 @@ import java.net.URI; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.api.ServerProblem; import io.github.belgif.rest.problem.i18n.I18N; @@ -13,7 +14,7 @@ * https://www.belgif.be/specification/rest/api-guide/#bad-gateway */ @ProblemType(BadGatewayProblem.TYPE) -public class BadGatewayProblem extends ServerProblem { +public class BadGatewayProblem extends ServerProblem implements FluentProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadRequestProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadRequestProblem.java index 81dcc9db..35509080 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadRequestProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/BadRequestProblem.java @@ -3,6 +3,7 @@ import java.net.URI; import java.util.List; +import io.github.belgif.rest.problem.api.FluentInputValidationProblem; import io.github.belgif.rest.problem.api.InputValidationIssue; import io.github.belgif.rest.problem.api.InputValidationProblem; import io.github.belgif.rest.problem.api.InvalidParam; @@ -17,7 +18,8 @@ * @see InputValidationProblem */ @ProblemType(BadRequestProblem.TYPE) -public class BadRequestProblem extends InputValidationProblem { +public class BadRequestProblem extends InputValidationProblem + implements FluentInputValidationProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/DefaultProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/DefaultProblem.java index 8d28eb85..869389ad 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/DefaultProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/DefaultProblem.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.Problem; import io.github.belgif.rest.problem.api.ProblemType; @@ -16,7 +17,7 @@ * with the matching problem type URI has been found on the classpath. *

*/ -public class DefaultProblem extends Problem { +public class DefaultProblem extends Problem implements FluentProblem { private static final long serialVersionUID = 1L; diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ExpiredAccessTokenProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ExpiredAccessTokenProblem.java index 8af3d5fb..7b6f985b 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ExpiredAccessTokenProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ExpiredAccessTokenProblem.java @@ -6,6 +6,7 @@ import java.util.Objects; import io.github.belgif.rest.problem.api.ClientProblem; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.HttpResponseHeaders; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.i18n.I18N; @@ -17,7 +18,8 @@ * https://www.belgif.be/specification/rest/api-guide/#expired-access-token */ @ProblemType(ExpiredAccessTokenProblem.TYPE) -public class ExpiredAccessTokenProblem extends ClientProblem implements HttpResponseHeaders { +public class ExpiredAccessTokenProblem extends ClientProblem + implements FluentProblem, HttpResponseHeaders { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InternalServerErrorProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InternalServerErrorProblem.java index 229e2f43..240e937f 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InternalServerErrorProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InternalServerErrorProblem.java @@ -2,6 +2,7 @@ import java.net.URI; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.api.ServerProblem; @@ -13,7 +14,7 @@ * https://www.belgif.be/specification/rest/api-guide/#internal-server-error */ @ProblemType(InternalServerErrorProblem.TYPE) -public class InternalServerErrorProblem extends ServerProblem { +public class InternalServerErrorProblem extends ServerProblem implements FluentProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InvalidAccessTokenProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InvalidAccessTokenProblem.java index 622f13a0..7f361d1d 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InvalidAccessTokenProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/InvalidAccessTokenProblem.java @@ -6,6 +6,7 @@ import java.util.Objects; import io.github.belgif.rest.problem.api.ClientProblem; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.HttpResponseHeaders; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.i18n.I18N; @@ -17,7 +18,8 @@ * https://www.belgif.be/specification/rest/api-guide/#invalid-access-token */ @ProblemType(InvalidAccessTokenProblem.TYPE) -public class InvalidAccessTokenProblem extends ClientProblem implements HttpResponseHeaders { +public class InvalidAccessTokenProblem extends ClientProblem + implements FluentProblem, HttpResponseHeaders { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingPermissionProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingPermissionProblem.java index ad125638..94ad8c95 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingPermissionProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingPermissionProblem.java @@ -2,6 +2,7 @@ import java.net.URI; +import io.github.belgif.rest.problem.api.FluentInputValidationProblem; import io.github.belgif.rest.problem.api.InputValidationIssue; import io.github.belgif.rest.problem.api.InputValidationProblem; import io.github.belgif.rest.problem.api.ProblemType; @@ -13,7 +14,8 @@ * https://www.belgif.be/specification/rest/api-guide/#missing-permission */ @ProblemType(MissingPermissionProblem.TYPE) -public class MissingPermissionProblem extends InputValidationProblem { +public class MissingPermissionProblem extends InputValidationProblem + implements FluentInputValidationProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingScopeProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingScopeProblem.java index 664bea14..864a810e 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingScopeProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/MissingScopeProblem.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonSetter; import io.github.belgif.rest.problem.api.ClientProblem; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.HttpResponseHeaders; import io.github.belgif.rest.problem.api.ProblemType; @@ -21,7 +22,8 @@ * https://www.belgif.be/specification/rest/api-guide/#missing-scope */ @ProblemType(MissingScopeProblem.TYPE) -public class MissingScopeProblem extends ClientProblem implements HttpResponseHeaders { +public class MissingScopeProblem extends ClientProblem + implements FluentProblem, HttpResponseHeaders { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/NoAccessTokenProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/NoAccessTokenProblem.java index 9eea4c63..8bc22cf1 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/NoAccessTokenProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/NoAccessTokenProblem.java @@ -6,6 +6,7 @@ import java.util.Objects; import io.github.belgif.rest.problem.api.ClientProblem; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.HttpResponseHeaders; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.i18n.I18N; @@ -17,7 +18,8 @@ * https://www.belgif.be/specification/rest/api-guide/#no-access-token */ @ProblemType(NoAccessTokenProblem.TYPE) -public class NoAccessTokenProblem extends ClientProblem implements HttpResponseHeaders { +public class NoAccessTokenProblem extends ClientProblem + implements FluentProblem, HttpResponseHeaders { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/PayloadTooLargeProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/PayloadTooLargeProblem.java index 16b55188..e0ef0c9e 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/PayloadTooLargeProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/PayloadTooLargeProblem.java @@ -4,6 +4,7 @@ import java.util.Objects; import io.github.belgif.rest.problem.api.ClientProblem; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.ProblemType; /** @@ -13,7 +14,7 @@ * https://www.belgif.be/specification/rest/api-guide/#payloadTooLargeProblem */ @ProblemType(PayloadTooLargeProblem.TYPE) -public class PayloadTooLargeProblem extends ClientProblem { +public class PayloadTooLargeProblem extends ClientProblem implements FluentProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ResourceNotFoundProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ResourceNotFoundProblem.java index 090a6b29..9e17acaa 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ResourceNotFoundProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ResourceNotFoundProblem.java @@ -2,6 +2,7 @@ import java.net.URI; +import io.github.belgif.rest.problem.api.FluentInputValidationProblem; import io.github.belgif.rest.problem.api.InEnum; import io.github.belgif.rest.problem.api.InputValidationIssue; import io.github.belgif.rest.problem.api.InputValidationProblem; @@ -17,7 +18,8 @@ * @see InputValidationProblem */ @ProblemType(ResourceNotFoundProblem.TYPE) -public class ResourceNotFoundProblem extends InputValidationProblem { +public class ResourceNotFoundProblem extends InputValidationProblem + implements FluentInputValidationProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ServiceUnavailableProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ServiceUnavailableProblem.java index f33dcbfd..d18bbf2b 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ServiceUnavailableProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/ServiceUnavailableProblem.java @@ -2,6 +2,7 @@ import java.net.URI; +import io.github.belgif.rest.problem.api.FluentRetryAfterProblem; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.api.RetryAfterServerProblem; @@ -12,7 +13,8 @@ * https://www.belgif.be/specification/rest/api-guide/#service-unavailable */ @ProblemType(ServiceUnavailableProblem.TYPE) -public class ServiceUnavailableProblem extends RetryAfterServerProblem { +public class ServiceUnavailableProblem extends RetryAfterServerProblem + implements FluentRetryAfterProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyFailedRequestsProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyFailedRequestsProblem.java index 751374db..8d0b870c 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyFailedRequestsProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyFailedRequestsProblem.java @@ -3,6 +3,7 @@ import java.net.URI; import java.util.Objects; +import io.github.belgif.rest.problem.api.FluentRetryAfterProblem; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.api.RetryAfterClientProblem; @@ -14,7 +15,8 @@ * https://www.belgif.be/specification/rest/api-guide/#too-many-failed-requests */ @ProblemType(TooManyFailedRequestsProblem.TYPE) -public class TooManyFailedRequestsProblem extends RetryAfterClientProblem { +public class TooManyFailedRequestsProblem extends RetryAfterClientProblem + implements FluentRetryAfterProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyRequestsProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyRequestsProblem.java index 19c9f803..91e7cf9d 100644 --- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyRequestsProblem.java +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/TooManyRequestsProblem.java @@ -3,6 +3,7 @@ import java.net.URI; import java.util.Objects; +import io.github.belgif.rest.problem.api.FluentRetryAfterProblem; import io.github.belgif.rest.problem.api.ProblemType; import io.github.belgif.rest.problem.api.RetryAfterClientProblem; @@ -13,7 +14,8 @@ * https://www.belgif.be/specification/rest/api-guide/#too-many-requests */ @ProblemType(TooManyRequestsProblem.TYPE) -public class TooManyRequestsProblem extends RetryAfterClientProblem { +public class TooManyRequestsProblem extends RetryAfterClientProblem + implements FluentRetryAfterProblem { /** * The problem type. diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentInputValidationProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentInputValidationProblem.java new file mode 100644 index 00000000..c3b84012 --- /dev/null +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentInputValidationProblem.java @@ -0,0 +1,28 @@ +package io.github.belgif.rest.problem.api; + +import java.util.List; + +/** + * Provides default methods with fluent InputValidationProblem properties (issues). + * + * @param the concrete InputValidationProblem self-type + */ +@SuppressWarnings("unchecked") +public interface FluentInputValidationProblem> + extends FluentProblem { + + void setIssues(InputValidationIssue... issues); + + void setIssues(List issues); + + default SELF issues(InputValidationIssue... issues) { + setIssues(issues); + return (SELF) this; + } + + default SELF issues(List issues) { + setIssues(issues); + return (SELF) this; + } + +} diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentProblem.java new file mode 100644 index 00000000..1cf40516 --- /dev/null +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentProblem.java @@ -0,0 +1,41 @@ +package io.github.belgif.rest.problem.api; + +import java.net.URI; + +/** + * Provides default methods with fluent Problem properties (detail, href, instance, additionalProperty). + * + * @param the concrete Problem self-type + */ +@SuppressWarnings("unchecked") +public interface FluentProblem> { + + void setDetail(String detail); + + void setHref(URI href); + + void setInstance(URI instance); + + void setAdditionalProperty(String name, Object value); + + default SELF detail(String detail) { + setDetail(detail); + return (SELF) this; + } + + default SELF href(URI href) { + setHref(href); + return (SELF) this; + } + + default SELF instance(URI instance) { + setInstance(instance); + return (SELF) this; + } + + default SELF additionalProperty(String name, Object value) { + setAdditionalProperty(name, value); + return (SELF) this; + } + +} diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentRetryAfterProblem.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentRetryAfterProblem.java new file mode 100644 index 00000000..f097763e --- /dev/null +++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/FluentRetryAfterProblem.java @@ -0,0 +1,28 @@ +package io.github.belgif.rest.problem.api; + +import java.time.OffsetDateTime; + +/** + * Provides default methods with fluent RetryAfter problem properties (retryAfter, retryAfterSec). + * + * @param the concrete RetryAfter Problem self-type + */ +@SuppressWarnings("unchecked") +public interface FluentRetryAfterProblem> + extends FluentProblem { + + void setRetryAfter(OffsetDateTime retryAfter); + + void setRetryAfterSec(Long retryAfterSec); + + default SELF retryAfter(OffsetDateTime retryAfter) { + setRetryAfter(retryAfter); + return (SELF) this; + } + + default SELF retryAfterSec(Long retryAfterSec) { + setRetryAfterSec(retryAfterSec); + return (SELF) this; + } + +} diff --git a/belgif-rest-problem/src/test/java/adoc/CodeSamples.java b/belgif-rest-problem/src/test/java/adoc/CodeSamples.java index 43ee5231..248d5397 100644 --- a/belgif-rest-problem/src/test/java/adoc/CodeSamples.java +++ b/belgif-rest-problem/src/test/java/adoc/CodeSamples.java @@ -6,6 +6,7 @@ import io.github.belgif.rest.problem.BadRequestProblem; import io.github.belgif.rest.problem.ResourceNotFoundProblem; import io.github.belgif.rest.problem.api.ClientProblem; +import io.github.belgif.rest.problem.api.FluentProblem; import io.github.belgif.rest.problem.api.InEnum; import io.github.belgif.rest.problem.api.InputValidationIssue; import io.github.belgif.rest.problem.api.Problem; @@ -15,7 +16,8 @@ public class CodeSamples { // tag::api-local-problem[] @ProblemType(TooManyResultsProblem.TYPE) - public static class TooManyResultsProblem extends ClientProblem { + public static class TooManyResultsProblem extends ClientProblem + implements FluentProblem { public static final String TYPE = "urn:problem-type:cbss:legallog:tooManyResults"; public static final URI TYPE_URI = URI.create(TYPE); diff --git a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/BadRequestProblemTest.java b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/BadRequestProblemTest.java index a85a20b2..c367ccd3 100644 --- a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/BadRequestProblemTest.java +++ b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/BadRequestProblemTest.java @@ -2,12 +2,14 @@ import static org.assertj.core.api.Assertions.*; +import java.net.URI; import java.util.Collections; import org.junit.jupiter.api.Test; import io.github.belgif.rest.problem.api.InEnum; import io.github.belgif.rest.problem.api.InputValidationIssue; +import io.github.belgif.rest.problem.api.InputValidationIssues; import io.github.belgif.rest.problem.api.InvalidParam; import io.github.belgif.rest.problem.api.ProblemType; @@ -72,6 +74,21 @@ void problemTypeAnnotation() { .isEqualTo("urn:problem-type:belgif:badRequest"); } + @Test + void fluentProperties() { + BadRequestProblem problem = new BadRequestProblem() + .detail("detail") + .issues(InputValidationIssues.requiredInput(InEnum.QUERY, "test")) + .href(URI.create("href")) + .instance(URI.create("instance")) + .additionalProperty("foo", "bar"); + assertThat(problem.getDetail()).isEqualTo("detail"); + assertThat(problem.getHref()).hasToString("href"); + assertThat(problem.getInstance()).hasToString("instance"); + assertThat(problem.getAdditionalProperties()).containsEntry("foo", "bar"); + assertThat(problem.getIssues()).containsExactly(InputValidationIssues.requiredInput(InEnum.QUERY, "test")); + } + @Test void equalsAndHashCode() { InputValidationIssue issue = new InputValidationIssue(InEnum.QUERY, "test"); diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index f6dacf4e..c1addb30 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -353,6 +353,7 @@ The https://www.belgif.be/specification/rest/api-guide/#rule-prb-type[naming con include::../../../belgif-rest-problem/src/test/java/adoc/CodeSamples.java[tag=api-local-problem] ---- +If you want fluent setter properties in your custom problem type, you can implement the `FluentProblem` interface. Your custom problem type can define additional properties if necessary. For problem types related to input parameters, you can extend from `InputValidationProblem` instead of `Problem`. diff --git a/src/main/asciidoc/release-notes.adoc b/src/main/asciidoc/release-notes.adoc index e822c662..88571935 100644 --- a/src/main/asciidoc/release-notes.adoc +++ b/src/main/asciidoc/release-notes.adoc @@ -20,6 +20,10 @@ * Don't include null (issue) input values when serializing * Replace `urn:problem-type:cbss:input-validation:referencedResourceNotFound` by standardized `urn:problem-type:belgif:input-validation:referencedResourceNotFound` +* Add fluent setter methods: +** Problem: detail, href, instance, additionalProperties +** InputValidationProblem: issues +** RetryAfterProblem: retryAfter, retryAfterSec * Improve WWW-Authenticate header for token-related problem types: ** Support setting the "realm" attribute ** Add "error_description" and "scope" attributes for missing_scope