diff --git a/api/src/main/java/org/apache/iceberg/exceptions/NoSuchWarehouseException.java b/api/src/main/java/org/apache/iceberg/exceptions/NoSuchWarehouseException.java new file mode 100644 index 000000000000..94ae50cd1c25 --- /dev/null +++ b/api/src/main/java/org/apache/iceberg/exceptions/NoSuchWarehouseException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iceberg.exceptions; + +import com.google.errorprone.annotations.FormatMethod; + +/** Exception raised when attempting to load a warehouse that does not exist. */ +public class NoSuchWarehouseException extends RuntimeException { + @FormatMethod + public NoSuchWarehouseException(String message, Object... args) { + super(String.format(message, args)); + } + + @FormatMethod + public NoSuchWarehouseException(Throwable cause, String message, Object... args) { + super(String.format(message, args), cause); + } +} diff --git a/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java b/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java index 791eb732bb7c..334bfde8abfc 100644 --- a/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java +++ b/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java @@ -30,6 +30,7 @@ import org.apache.iceberg.exceptions.NoSuchPlanTaskException; import org.apache.iceberg.exceptions.NoSuchTableException; import org.apache.iceberg.exceptions.NoSuchViewException; +import org.apache.iceberg.exceptions.NoSuchWarehouseException; import org.apache.iceberg.exceptions.NotAuthorizedException; import org.apache.iceberg.exceptions.NotFoundException; import org.apache.iceberg.exceptions.RESTException; @@ -92,6 +93,10 @@ public static Consumer defaultErrorHandler() { return DefaultErrorHandler.INSTANCE; } + public static Consumer configErrorHandler() { + return ConfigErrorHandler.INSTANCE; + } + public static Consumer oauthErrorHandler() { return OAuthErrorHandler.INSTANCE; } @@ -295,6 +300,20 @@ public void accept(ErrorResponse error) { } } + /** Request error handler for config endpoint. */ + private static class ConfigErrorHandler extends DefaultErrorHandler { + private static final ErrorHandler INSTANCE = new ConfigErrorHandler(); + + @Override + public void accept(ErrorResponse error) { + if (error.code() == 404 && error.type() != null) { + throw new NoSuchWarehouseException("%s", error.message()); + } + + super.accept(error); + } + } + /** * Request error handler that handles the common cases that are included with all responses, such * as 400, 500, etc. diff --git a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java index c7b5b5d41c74..ec30d9de897e 100644 --- a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java +++ b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java @@ -1338,7 +1338,7 @@ private static ConfigResponse fetchConfig( queryParams.build(), ConfigResponse.class, RESTUtil.configHeaders(properties), - ErrorHandlers.defaultErrorHandler()); + ErrorHandlers.configErrorHandler()); configResponse.validate(); return configResponse; } diff --git a/core/src/test/java/org/apache/iceberg/rest/TestErrorHandlers.java b/core/src/test/java/org/apache/iceberg/rest/TestErrorHandlers.java index 8bf62c3c6cf5..b7bbe337cd27 100644 --- a/core/src/test/java/org/apache/iceberg/rest/TestErrorHandlers.java +++ b/core/src/test/java/org/apache/iceberg/rest/TestErrorHandlers.java @@ -20,7 +20,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.apache.iceberg.exceptions.NoSuchWarehouseException; import org.apache.iceberg.exceptions.RESTException; +import org.apache.iceberg.exceptions.ServiceFailureException; import org.apache.iceberg.rest.responses.ErrorResponse; import org.junit.jupiter.api.Test; @@ -68,4 +70,38 @@ public void errorHandlerWithCodeAndTypeOnly() { .isInstanceOf(RESTException.class) .hasMessage("Unable to process (code: 422, type: ValidationException): null"); } + + @Test + public void testConfigErrorHandler404ThrowsNoSuchWarehouseException() { + ErrorResponse error = + ErrorResponse.builder() + .responseCode(404) + .withType("NotFoundException") + .withMessage("Warehouse not found") + .build(); + + assertThatThrownBy(() -> ErrorHandlers.configErrorHandler().accept(error)) + .isInstanceOf(NoSuchWarehouseException.class) + .hasMessage("Warehouse not found"); + } + + @Test + public void testConfigErrorHandler404ForMisconfiguredUri() { + ErrorResponse error = + ErrorResponse.builder().responseCode(404).withMessage("Not Found").build(); + + assertThatThrownBy(() -> ErrorHandlers.configErrorHandler().accept(error)) + .isInstanceOf(RESTException.class) + .hasMessageContaining("Not Found"); + } + + @Test + public void testConfigErrorHandlerDelegatesToDefaultForNon404() { + ErrorResponse error = + ErrorResponse.builder().responseCode(500).withMessage("Internal server error").build(); + + assertThatThrownBy(() -> ErrorHandlers.configErrorHandler().accept(error)) + .isInstanceOf(ServiceFailureException.class) + .hasMessageContaining("Internal server error"); + } }