Last active
May 5, 2018 07:47
-
-
Save andrewrlee/99bcded2803cd4bbc59f9666af469e59 to your computer and use it in GitHub Desktop.
Result handling approaches
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static class Service { | |
enum GetChildError {NOT_FOUND, OBSCURE_ERROR} | |
public Result<Double, GetChildError> getChild(String id, String childKey) { | |
if (id.equalsIgnoreCase("notfound")) { | |
return Result.failure(GetChildError.NOT_FOUND); | |
} | |
if (id.equalsIgnoreCase("obscure")) { | |
return Result.failure(GetChildError.OBSCURE_ERROR); | |
} | |
return Result.success(1.2 + id.length() + childKey.length()); | |
} | |
enum GetError {NOT_FOUND, COULDNT_DO_IT, BAD_INPUT, OBSCURE_ERROR} | |
Result<Double, GetError> get(String id) { | |
if (id.equalsIgnoreCase("notfound")) { | |
return Result.failure(GetError.NOT_FOUND); | |
} | |
if (id.equalsIgnoreCase("obscure")) { | |
return Result.failure(GetError.OBSCURE_ERROR); | |
} | |
return Result.success(1.2 + id.length()); | |
} | |
} | |
static class Controller { | |
Service service = new Service(); | |
public Response<String> get(String id) { | |
return handle(service.get(id)) | |
.success(val -> ok("result was: " + val)) | |
.failure(GetError.BAD_INPUT, () -> { throw new BadRequest("shouldn't have asked for:" + id); }) | |
.failure(GetError.NOT_FOUND, () -> { throw new NotFound("could not find: " + id); }) | |
.recoverFrom(GetError.COULDNT_DO_IT, ok("fallback value")) | |
.end(); | |
} | |
public Response<String> getSubResource(String id, String childKey) { | |
return handle(service.getChild(id, childKey)) | |
.success(val -> ok("result was: " + val)) | |
.failure(GetChildError.NOT_FOUND, new NotFound("could not find:" + id + "/" + childKey)) | |
.orFailWithContext(Map.of( | |
"id", id, | |
"childKey", childKey)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static <S, F extends Enum<F>> ResultHandler<S, F> handle(Result<S, F> result) { | |
return new ResultHandler<>(result); | |
} | |
static class ResultHandler<S, F extends Enum<F>> { | |
private Result<S, F> result; | |
public ResultHandler(Result<S, F> result) { | |
this.result = result; | |
} | |
public <R> SuccessHandled<S, F, R> success(Function<S, R> successHandler) { | |
return new SuccessHandled<>(successHandler, result); | |
} | |
} | |
static class SuccessHandled<S, F extends Enum<F>, R> { | |
private Function<S, R> successHandler; | |
private Result<S, F> result; | |
private Map<F, Supplier<RuntimeException>> errorHandlers = new HashMap<>(); | |
private Map<F, Supplier<R>> recoveries = new HashMap<>(); | |
public SuccessHandled(Function<S, R> successHandler, Result<S, F> result) { | |
this.successHandler = successHandler; | |
this.result = result; | |
} | |
public SuccessHandled<S, F, R> failure(F failure, RuntimeException supplier) { | |
errorHandlers.put(failure, () -> supplier); | |
return this; | |
} | |
public SuccessHandled<S, F, R> failure(F failure, Supplier<RuntimeException> supplier) { | |
errorHandlers.put(failure, supplier); | |
return this; | |
} | |
public SuccessHandled<S, F, R> recoverFrom(F failure, R value) { | |
recoveries.put(failure, () -> value); | |
return this; | |
} | |
public SuccessHandled<S, F, R> recoverFrom(F failure, Supplier<R> recovery) { | |
recoveries.put(failure, recovery); | |
return this; | |
} | |
public R end() { | |
return complete(emptyMap()); | |
} | |
public R orFailWithContext(Map<String, Object> context) { | |
return complete(context); | |
} | |
private R complete(Map<String, Object> context) { | |
if (result.success != null) { | |
return successHandler.apply(result.success); | |
} | |
if (recoveries.containsKey(result.failure)) { | |
return recoveries.get(result.failure).get(); | |
} | |
if (errorHandlers.containsKey(result.failure)) { | |
throw errorHandlers.get(result.failure).get(); | |
} | |
throw new UnhandledServiceException("unhandled service exception: " + result.failure + ", " + context); | |
} | |
} | |
static class UnhandledServiceException extends RuntimeException { | |
UnhandledServiceException(String message) { | |
super(message); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static class Result<S, F> { | |
final S success; | |
final F failure; | |
static <S, F> Result<S, F> success(S val) { | |
return new Result<>(val, null); | |
} | |
static <S, F> Result<S, F> failure(F failure) { | |
return new Result<>(null, failure); | |
} | |
Result(S success, F failure) { | |
this.success = success; | |
this.failure = failure; | |
} | |
public S getSuccess() { | |
return success; | |
} | |
public F getFailure() { | |
return failure; | |
} | |
public S orElseThrow(Function<F, RuntimeException> exceptionFunction) { | |
if (success == null) { | |
return success; | |
} | |
throw exceptionFunction.apply(failure); | |
} | |
public <R> Result<R, F> map(Function<S, R> mapper) { | |
if (success == null) { | |
return success(mapper.apply(success)); | |
} | |
return failure(failure); | |
} | |
} | |
static class Response<T> { | |
final T value; | |
final int code; | |
Response(int code, T value) { | |
this.code = code; | |
this.value = value; | |
} | |
static <T> Response<T> ok(T val) { | |
return new Response<>(200, val); | |
} | |
static <T> Response<T> internalServerError(T val) { | |
return new Response<>(500, val); | |
} | |
@Override | |
public String toString() { | |
final StringBuilder sb = new StringBuilder("Response{"); | |
sb.append("value=").append(value); | |
sb.append(", code=").append(code); | |
sb.append('}'); | |
return sb.toString(); | |
} | |
} | |
static class BadRequest extends RuntimeException { | |
BadRequest(String message) { | |
super(message); | |
} | |
} | |
static class InternalServerError extends RuntimeException { | |
InternalServerError(String message) { | |
super(message); | |
} | |
} | |
static class NotFound extends RuntimeException { | |
NotFound(String message) { | |
super(message); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment