Skip to content

Commit

Permalink
Bmoric/restore old interface (#21235)
Browse files Browse the repository at this point in the history
* Re introduce old exception mapper

* Use mapper
  • Loading branch information
benmoriceau authored Jan 11, 2023
1 parent 59ff2a2 commit 449b252
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,7 @@
import io.airbyte.persistence.job.errorreporter.JobErrorReportingClientFactory;
import io.airbyte.persistence.job.factory.OAuthConfigSupplier;
import io.airbyte.persistence.job.tracker.JobTracker;
import io.airbyte.server.errors.InvalidInputExceptionMapper;
import io.airbyte.server.errors.InvalidJsonExceptionMapper;
import io.airbyte.server.errors.InvalidJsonInputExceptionMapper;
import io.airbyte.server.errors.KnownExceptionMapper;
import io.airbyte.server.errors.NotFoundExceptionMapper;
import io.airbyte.server.errors.UncaughtExceptionMapper;
import io.airbyte.server.errors.*;
import io.airbyte.server.handlers.*;
import io.airbyte.server.scheduler.DefaultSynchronousSchedulerClient;
import io.airbyte.server.scheduler.EventRunner;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.server.errors;

import io.airbyte.api.model.generated.InvalidInputExceptionInfo;
import io.airbyte.api.model.generated.InvalidInputProperty;
import io.airbyte.commons.json.Jsons;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.logging.log4j.core.util.Throwables;

// https://www.baeldung.com/jersey-bean-validation#custom-exception-handler
// handles exceptions related to the request body not matching the openapi config.
@Produces
@Singleton
@Requires(classes = ConstraintViolationException.class)
public class InvalidInputExceptionHandler implements ExceptionHandler<ConstraintViolationException, HttpResponse> {

public static InvalidInputExceptionInfo infoFromConstraints(final ConstraintViolationException cve) {
final InvalidInputExceptionInfo exceptionInfo = new InvalidInputExceptionInfo()
.exceptionClassName(cve.getClass().getName())
.message("Some properties contained invalid input.")
.exceptionStack(Throwables.toStringList(cve));

final List<InvalidInputProperty> props = new ArrayList<InvalidInputProperty>();
for (final ConstraintViolation<?> cv : cve.getConstraintViolations()) {
props.add(new InvalidInputProperty()
.propertyPath(cv.getPropertyPath().toString())
.message(cv.getMessage())
.invalidValue(cv.getInvalidValue() != null ? cv.getInvalidValue().toString() : "null"));
}
exceptionInfo.validationErrors(props);
return exceptionInfo;
}

@Override
public HttpResponse handle(final HttpRequest request, final ConstraintViolationException exception) {
return HttpResponse.status(HttpStatus.BAD_REQUEST)
.body(Jsons.serialize(InvalidInputExceptionHandler.infoFromConstraints(exception)))
.contentType(MediaType.APPLICATION_JSON_TYPE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,21 @@

package io.airbyte.server.errors;

import io.airbyte.api.model.generated.InvalidInputExceptionInfo;
import io.airbyte.api.model.generated.InvalidInputProperty;
import io.airbyte.commons.json.Jsons;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.logging.log4j.core.util.Throwables;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

// https://www.baeldung.com/jersey-bean-validation#custom-exception-handler
// handles exceptions related to the request body not matching the openapi config.
@Produces
@Singleton
@Requires(classes = ConstraintViolationException.class)
public class InvalidInputExceptionMapper implements ExceptionHandler<ConstraintViolationException, HttpResponse> {

public static InvalidInputExceptionInfo infoFromConstraints(final ConstraintViolationException cve) {
final InvalidInputExceptionInfo exceptionInfo = new InvalidInputExceptionInfo()
.exceptionClassName(cve.getClass().getName())
.message("Some properties contained invalid input.")
.exceptionStack(Throwables.toStringList(cve));

final List<InvalidInputProperty> props = new ArrayList<InvalidInputProperty>();
for (final ConstraintViolation<?> cv : cve.getConstraintViolations()) {
props.add(new InvalidInputProperty()
.propertyPath(cv.getPropertyPath().toString())
.message(cv.getMessage())
.invalidValue(cv.getInvalidValue() != null ? cv.getInvalidValue().toString() : "null"));
}
exceptionInfo.validationErrors(props);
return exceptionInfo;
}
@Provider
public class InvalidInputExceptionMapper implements ExceptionMapper<ConstraintViolationException> {

@Override
public HttpResponse handle(final HttpRequest request, final ConstraintViolationException exception) {
return HttpResponse.status(HttpStatus.BAD_REQUEST)
.body(Jsons.serialize(InvalidInputExceptionMapper.infoFromConstraints(exception)))
.contentType(MediaType.APPLICATION_JSON_TYPE);
public Response toResponse(final ConstraintViolationException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(Jsons.serialize(InvalidInputExceptionHandler.infoFromConstraints(e)))
.type("application/json")
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.server.errors;

import com.fasterxml.jackson.core.JsonParseException;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;

@Produces
@Singleton
@Requires(classes = JsonParseException.class)
public class InvalidJsonExceptionHandler implements ExceptionHandler<JsonParseException, HttpResponse> {

@Override
public HttpResponse handle(final HttpRequest request, final JsonParseException exception) {
return HttpResponse.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(
KnownException.infoFromThrowableWithMessage(exception, "Invalid json. " + exception.getMessage() + " " + exception.getOriginalMessage()))
.contentType(MediaType.APPLICATION_JSON_TYPE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,19 @@
package io.airbyte.server.errors;

import com.fasterxml.jackson.core.JsonParseException;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Produces
@Singleton
@Requires(classes = JsonParseException.class)
public class InvalidJsonExceptionMapper implements ExceptionHandler<JsonParseException, HttpResponse> {
@Provider
public class InvalidJsonExceptionMapper implements ExceptionMapper<JsonParseException> {

@Override
public HttpResponse handle(final HttpRequest request, final JsonParseException exception) {
return HttpResponse.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(
KnownException.infoFromThrowableWithMessage(exception, "Invalid json. " + exception.getMessage() + " " + exception.getOriginalMessage()))
.contentType(MediaType.APPLICATION_JSON_TYPE);
public Response toResponse(final JsonParseException e) {
return Response.status(422)
.entity(KnownException.infoFromThrowableWithMessage(e, "Invalid json. " + e.getMessage() + " " + e.getOriginalMessage()))
.type("application/json")
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.server.errors;

import com.fasterxml.jackson.databind.JsonMappingException;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;

@Produces
@Singleton
@Requires(classes = JsonMappingException.class)
public class InvalidJsonInputExceptionHandler implements ExceptionHandler<JsonMappingException, HttpResponse> {

@Override
public HttpResponse handle(final HttpRequest request, final JsonMappingException exception) {
return HttpResponse.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(KnownException.infoFromThrowableWithMessage(exception,
"Invalid json input. " + exception.getMessage() + " " + exception.getOriginalMessage()))
.contentType(MediaType.APPLICATION_JSON_TYPE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,21 @@
package io.airbyte.server.errors;

import com.fasterxml.jackson.databind.JsonMappingException;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
import io.airbyte.commons.json.Jsons;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Produces
@Singleton
@Requires(classes = JsonMappingException.class)
public class InvalidJsonInputExceptionMapper implements ExceptionHandler<JsonMappingException, HttpResponse> {
@Provider
public class InvalidJsonInputExceptionMapper implements ExceptionMapper<JsonMappingException> {

@Override
public HttpResponse handle(final HttpRequest request, final JsonMappingException exception) {
return HttpResponse.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(KnownException.infoFromThrowableWithMessage(exception,
"Invalid json input. " + exception.getMessage() + " " + exception.getOriginalMessage()))
.contentType(MediaType.APPLICATION_JSON_TYPE);
public Response toResponse(final JsonMappingException e) {
return Response.status(422)
.entity(
Jsons.serialize(KnownException.infoFromThrowableWithMessage(e, "Invalid json input. " + e.getMessage() + " " + e.getOriginalMessage())))
.type("application/json")
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.server.errors;

import io.airbyte.commons.json.Jsons;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;

@Produces
@Singleton
@Requires(classes = KnownException.class)
public class KnownExceptionHandler implements ExceptionHandler<KnownException, HttpResponse> {

@Override
public HttpResponse handle(HttpRequest request, KnownException exception) {
return HttpResponse.status(HttpStatus.valueOf(exception.getHttpCode()))
.body(Jsons.serialize(exception.getKnownExceptionInfo()))
.contentType(MediaType.APPLICATION_JSON_TYPE);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2022 Airbyte, Inc., all rights reserved.
*/

package io.airbyte.server.errors;

import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
import javax.ws.rs.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Produces
@Singleton
@Requires(classes = NotFoundException.class)
public class NotFoundExceptionHandler implements ExceptionHandler<NotFoundException, HttpResponse> {

private static final Logger LOGGER = LoggerFactory.getLogger(NotFoundExceptionHandler.class);

@Override
public HttpResponse handle(final HttpRequest request, final NotFoundException exception) {
final IdNotFoundKnownException idnf = new IdNotFoundKnownException("Object not found. " + exception.getMessage(), exception);
LOGGER.error("Not found exception", idnf.getNotFoundKnownExceptionInfo());

return HttpResponse.status(HttpStatus.NOT_FOUND)
.body(KnownException.infoFromThrowableWithMessage(exception, "Internal Server Error: " + exception.getMessage()))
.contentType(MediaType.APPLICATION_JSON);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,29 @@

package io.airbyte.server.errors;

import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
import io.airbyte.commons.json.Jsons;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Produces
@Singleton
@Requires(classes = NotFoundException.class)
public class NotFoundExceptionMapper implements ExceptionHandler<NotFoundException, HttpResponse> {
@Provider
public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {

private static final Logger LOGGER = LoggerFactory.getLogger(NotFoundExceptionMapper.class);

@Override
public HttpResponse handle(final HttpRequest request, final NotFoundException exception) {
final IdNotFoundKnownException idnf = new IdNotFoundKnownException("Object not found. " + exception.getMessage(), exception);
public Response toResponse(final NotFoundException e) {
// Would like to send the id along but we don't have access to the http request anymore to fetch it
// from. TODO: Come back to this with issue #4189
final IdNotFoundKnownException idnf = new IdNotFoundKnownException("Object not found. " + e.getMessage(), e);
LOGGER.error("Not found exception", idnf.getNotFoundKnownExceptionInfo());

return HttpResponse.status(HttpStatus.NOT_FOUND)
.body(KnownException.infoFromThrowableWithMessage(exception, "Internal Server Error: " + exception.getMessage()))
.contentType(MediaType.APPLICATION_JSON);
return Response.status(404)
.entity(Jsons.serialize(idnf.getNotFoundKnownExceptionInfo()))
.type("application/json")
.build();
}

}
Loading

0 comments on commit 449b252

Please sign in to comment.