This is an automated email from the ASF dual-hosted git repository.

roryqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 4ac4ea6002 [#14240] feat(api): add ServiceUnavailableException mapped 
to HTTP 503 (#14241)
4ac4ea6002 is described below

commit 4ac4ea60026a3fc1e58d3346a9916feda95304b1
Author: MaSai <[email protected]>
AuthorDate: Thu Oct 29 09:54:14 2026 +0800

    [#14240] feat(api): add ServiceUnavailableException mapped to HTTP 503 
(#14241)
    
    ### What changes were proposed in this pull request?
    
    - Add `org.apache.gravitino.exceptions.ServiceUnavailableException`.
    - Add `ErrorConstants.SERVICE_UNAVAILABLE_CODE` (1013),
    `ErrorResponse.serviceUnavailable(...)`, and
    `Utils.serviceUnavailable(message[, throwable])` returning **HTTP 503**.
    - Map the exception in `ExceptionHandlers` (`BaseExceptionHandler`,
    catalog, and secret handlers).
    - Reconstruct `ServiceUnavailableException` in the Java client
    `ErrorHandlers` / `RestErrorHandler`.
    
    ### Why are the changes needed?
    
    `ConnectionFailedException` maps to HTTP 502, which fits federated
    catalog backends. Temporarily unavailable dependencies (for example a
    secret provider) should surface as **HTTP 503** so callers can tell
    retryable outages from gateway failures and from 500s.
    
    Fix: #14240
    
    ### Does this PR introduce any user-facing change?
    
    Yes:
    - New public exception type and REST error code.
    - APIs that throw `ServiceUnavailableException` return HTTP 503 with a
    structured `ErrorResponse`.
    
    ### How was this patch tested?
    
    ```
    ./gradlew :common:test --tests 
'org.apache.gravitino.dto.responses.TestResponses' \
      :server-common:test --tests 'org.apache.gravitino.server.web.TestUtils' \
      :server:test --tests 
'org.apache.gravitino.server.web.rest.TestExceptionHandlers' \
      :clients:client-java:test --tests 
'org.apache.gravitino.client.TestErrorHandlers'
    ```
    
    ---------
    
    Co-authored-by: Cursor <[email protected]>
---
 .../exceptions/ServiceUnavailableException.java    | 49 ++++++++++++++++++++++
 .../org/apache/gravitino/client/ErrorHandlers.java |  4 ++
 .../apache/gravitino/client/TestErrorHandlers.java | 28 ++++++++++++-
 clients/client-python/gravitino/constants/error.py |  5 +++
 clients/client-python/gravitino/exceptions/base.py |  4 ++
 .../tests/unittests/test_error_handler.py          | 25 +++++++++++
 .../gravitino/dto/responses/ErrorConstants.java    |  3 ++
 .../gravitino/dto/responses/ErrorResponse.java     | 26 ++++++++++++
 .../gravitino/dto/responses/TestResponses.java     |  1 +
 .../org/apache/gravitino/server/web/Utils.java     | 14 +++++++
 .../org/apache/gravitino/server/web/TestUtils.java | 10 +++++
 .../server/web/rest/ExceptionHandlers.java         | 29 ++++++++++++-
 .../server/web/rest/TestExceptionHandlers.java     | 18 ++++++++
 13 files changed, 213 insertions(+), 3 deletions(-)

diff --git 
a/api/src/main/java/org/apache/gravitino/exceptions/ServiceUnavailableException.java
 
b/api/src/main/java/org/apache/gravitino/exceptions/ServiceUnavailableException.java
new file mode 100644
index 0000000000..40e6b7d2a5
--- /dev/null
+++ 
b/api/src/main/java/org/apache/gravitino/exceptions/ServiceUnavailableException.java
@@ -0,0 +1,49 @@
+/*
+ * 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.gravitino.exceptions;
+
+import com.google.errorprone.annotations.FormatMethod;
+import com.google.errorprone.annotations.FormatString;
+
+/** An exception thrown when a configured dependency is temporarily 
unavailable. */
+public class ServiceUnavailableException extends GravitinoRuntimeException {
+  /**
+   * Constructs a new exception with the specified detail message.
+   *
+   * @param cause the cause.
+   * @param errorMessageTemplate the detail message.
+   * @param args the arguments to the message.
+   */
+  @FormatMethod
+  public ServiceUnavailableException(
+      Throwable cause, @FormatString String errorMessageTemplate, Object... 
args) {
+    super(cause, errorMessageTemplate, args);
+  }
+
+  /**
+   * Constructs a new exception with the specified detail message.
+   *
+   * @param errorMessageTemplate the detail message.
+   * @param args the arguments to the message.
+   */
+  @FormatMethod
+  public ServiceUnavailableException(@FormatString String 
errorMessageTemplate, Object... args) {
+    super(errorMessageTemplate, args);
+  }
+}
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
index 6b35b5e016..35cb952990 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java
@@ -83,6 +83,7 @@ import org.apache.gravitino.exceptions.RESTException;
 import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
 import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
 import org.apache.gravitino.exceptions.SemanticModelAlreadyExistsException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
 import org.apache.gravitino.exceptions.TableAlreadyExistsException;
 import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
 import org.apache.gravitino.exceptions.TagAlreadyExistsException;
@@ -1439,6 +1440,9 @@ public class ErrorHandlers {
       if (errorResponse.getCode() == 
ErrorConstants.OPTIMISTIC_LOCK_CONFLICT_CODE) {
         throw new OptimisticLockException("%s", 
formatErrorMessage(errorResponse));
       }
+      if (errorResponse.getCode() == ErrorConstants.SERVICE_UNAVAILABLE_CODE) {
+        throw new ServiceUnavailableException("%s", 
formatErrorMessage(errorResponse));
+      }
       if (errorResponse.getCode() == ErrorConstants.CONNECTION_FAILED_CODE) {
         throw new ConnectionFailedException("%s", 
formatErrorMessage(errorResponse));
       }
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
index 0416faca8c..9723345fa5 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestErrorHandlers.java
@@ -26,12 +26,38 @@ import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.apache.gravitino.dto.responses.ErrorResponse;
 import org.apache.gravitino.exceptions.OptimisticLockException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-/** Tests REST error handling for optimistic-lock conflicts. */
+/** Tests REST error handling for cross-cutting error codes. */
 public class TestErrorHandlers {
 
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testServiceUnavailableAcrossHandlers() throws 
ReflectiveOperationException {
+    ErrorResponse response =
+        ErrorResponse.serviceUnavailable("secret provider 'vault' is 
unavailable");
+    List<Method> factories =
+        Arrays.stream(ErrorHandlers.class.getDeclaredMethods())
+            .filter(method -> Modifier.isPublic(method.getModifiers()))
+            .filter(method -> Modifier.isStatic(method.getModifiers()))
+            .filter(method -> method.getName().endsWith("ErrorHandler"))
+            .filter(method -> method.getParameterCount() == 0)
+            .filter(method -> 
Consumer.class.isAssignableFrom(method.getReturnType()))
+            .collect(Collectors.toList());
+    Assertions.assertFalse(factories.isEmpty());
+
+    for (Method factory : factories) {
+      Consumer<ErrorResponse> handler = (Consumer<ErrorResponse>) 
factory.invoke(null);
+      ServiceUnavailableException exception =
+          Assertions.assertThrows(
+              ServiceUnavailableException.class, () -> 
handler.accept(response), factory.getName());
+      Assertions.assertEquals(
+          "secret provider 'vault' is unavailable", exception.getMessage(), 
factory.getName());
+    }
+  }
+
   @Test
   @SuppressWarnings("unchecked")
   public void testOptimisticLockConflictAcrossHandlers() throws 
ReflectiveOperationException {
diff --git a/clients/client-python/gravitino/constants/error.py 
b/clients/client-python/gravitino/constants/error.py
index c63b8a9d09..6c31185220 100644
--- a/clients/client-python/gravitino/constants/error.py
+++ b/clients/client-python/gravitino/constants/error.py
@@ -30,6 +30,7 @@ from gravitino.exceptions.base import (
     NotInUseException,
     InUseException,
     OptimisticLockException,
+    ServiceUnavailableException,
     UnauthorizedException,
 )
 
@@ -76,6 +77,9 @@ class ErrorConstants(IntEnum):
     # Error codes for optimistic-lock conflicts.
     OPTIMISTIC_LOCK_CONFLICT_CODE = 1012
 
+    # Error codes for service unavailable.
+    SERVICE_UNAVAILABLE_CODE = 1013
+
     # Error codes for invalid state.
     UNKNOWN_ERROR_CODE = 1100
 
@@ -93,6 +97,7 @@ EXCEPTION_MAPPING = {
     NotInUseException: ErrorConstants.NOT_IN_USE_CODE,
     InUseException: ErrorConstants.IN_USE_CODE,
     OptimisticLockException: ErrorConstants.OPTIMISTIC_LOCK_CONFLICT_CODE,
+    ServiceUnavailableException: ErrorConstants.SERVICE_UNAVAILABLE_CODE,
     UnauthorizedException: ErrorConstants.UNAUTHORIZED_CODE,
 }
 
diff --git a/clients/client-python/gravitino/exceptions/base.py 
b/clients/client-python/gravitino/exceptions/base.py
index d632747d83..db9061ad65 100644
--- a/clients/client-python/gravitino/exceptions/base.py
+++ b/clients/client-python/gravitino/exceptions/base.py
@@ -157,6 +157,10 @@ class ConnectionFailedException(GravitinoRuntimeException):
     """An exception thrown when connect to catalog failed."""
 
 
+class ServiceUnavailableException(GravitinoRuntimeException):
+    """An exception thrown when a configured dependency is temporarily 
unavailable."""
+
+
 class OptimisticLockException(GravitinoRuntimeException):
     """Raised when another update changes an entity before a write 
completes."""
 
diff --git a/clients/client-python/tests/unittests/test_error_handler.py 
b/clients/client-python/tests/unittests/test_error_handler.py
index 9ccc2e370a..f2bf283627 100644
--- a/clients/client-python/tests/unittests/test_error_handler.py
+++ b/clients/client-python/tests/unittests/test_error_handler.py
@@ -59,6 +59,7 @@ from gravitino.exceptions.base import (
     RoleAlreadyExistsException,
     SchemaAlreadyExistsException,
     SemanticModelAlreadyExistsException,
+    ServiceUnavailableException,
     TableAlreadyExistsException,
     UnauthorizedException,
     UnsupportedOperationException,
@@ -203,6 +204,30 @@ class TestErrorHandler(unittest.TestCase):
                 ):
                     handler.handle(response)
 
+    def test_service_unavailable(self):
+        response = ErrorResponse.from_json(
+            '{"code":1013,"type":"ServiceUnavailableException",'
+            '"message":"Service unavailable","stack":null}'
+        )
+        generated = ErrorResponse.generate_error_response(
+            ServiceUnavailableException, "Service unavailable"
+        )
+        self.assertEqual(1013, generated.code())
+
+        for handler in (
+            REST_ERROR_HANDLER,
+            TABLE_ERROR_HANDLER,
+            VIEW_ERROR_HANDLER,
+            PARTITION_ERROR_HANDLER,
+            STATISTICS_ERROR_HANDLER,
+            CATALOG_ERROR_HANDLER,
+        ):
+            with self.subTest(handler=type(handler).__name__):
+                with self.assertRaisesRegex(
+                    ServiceUnavailableException, "Service unavailable"
+                ):
+                    handler.handle(response)
+
     def test_rest_error_handler(self):
         with self.assertRaises(RESTException):
             REST_ERROR_HANDLER.handle(
diff --git 
a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java 
b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
index 8b94b0df3e..8a241fb211 100644
--- 
a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
+++ 
b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorConstants.java
@@ -60,6 +60,9 @@ public class ErrorConstants {
   /** Error codes for optimistic-lock conflicts. */
   public static final int OPTIMISTIC_LOCK_CONFLICT_CODE = 1012;
 
+  /** Error codes for service unavailable. */
+  public static final int SERVICE_UNAVAILABLE_CODE = 1013;
+
   /** Error codes for invalid state. */
   public static final int UNKNOWN_ERROR_CODE = 1100;
 
diff --git 
a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java 
b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
index f537ccf6ee..59be69c809 100644
--- a/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
+++ b/common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java
@@ -30,6 +30,7 @@ import lombok.Getter;
 import org.apache.gravitino.exceptions.ConnectionFailedException;
 import org.apache.gravitino.exceptions.ForbiddenException;
 import org.apache.gravitino.exceptions.RESTException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
 
 /** Represents an error response. */
 @Getter
@@ -161,6 +162,31 @@ public class ErrorResponse extends BaseResponse {
         getStackTrace(throwable));
   }
 
+  /**
+   * Create a new service unavailable error instance of {@link ErrorResponse}.
+   *
+   * @param message The message of the error.
+   * @return The new instance.
+   */
+  public static ErrorResponse serviceUnavailable(String message) {
+    return serviceUnavailable(message, null);
+  }
+
+  /**
+   * Create a new service unavailable error instance of {@link ErrorResponse}.
+   *
+   * @param message The message of the error.
+   * @param throwable The throwable that caused the error.
+   * @return The new instance.
+   */
+  public static ErrorResponse serviceUnavailable(String message, Throwable 
throwable) {
+    return new ErrorResponse(
+        ErrorConstants.SERVICE_UNAVAILABLE_CODE,
+        ServiceUnavailableException.class.getSimpleName(),
+        message,
+        getStackTrace(throwable));
+  }
+
   /**
    * Create a new not found error instance of {@link ErrorResponse}.
    *
diff --git 
a/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java 
b/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
index ec58f95593..235181c25f 100644
--- a/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
+++ b/common/src/test/java/org/apache/gravitino/dto/responses/TestResponses.java
@@ -243,6 +243,7 @@ public class TestResponses {
     ErrorResponse[] responses = {
       ErrorResponse.illegalArguments(message, throwable),
       ErrorResponse.connectionFailed(message, throwable),
+      ErrorResponse.serviceUnavailable(message, throwable),
       ErrorResponse.notFound("error type", message, throwable),
       ErrorResponse.internalError(message, throwable),
       ErrorResponse.alreadyExists("error type", message, throwable),
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java 
b/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
index eadcae8d05..b90e355de6 100644
--- a/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
+++ b/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
@@ -130,6 +130,20 @@ public class Utils {
         .build();
   }
 
+  public static Response serviceUnavailable(String message) {
+    return Response.status(Response.Status.SERVICE_UNAVAILABLE)
+        .entity(ErrorResponse.serviceUnavailable(message))
+        .type(MediaType.APPLICATION_JSON)
+        .build();
+  }
+
+  public static Response serviceUnavailable(String message, Throwable 
throwable) {
+    return Response.status(Response.Status.SERVICE_UNAVAILABLE)
+        .entity(ErrorResponse.serviceUnavailable(message, throwable))
+        .type(MediaType.APPLICATION_JSON)
+        .build();
+  }
+
   public static Response internalError(String message) {
     return internalError(message, null);
   }
diff --git 
a/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java 
b/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
index 52618dfcd8..9a6d44270c 100644
--- a/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
+++ b/server-common/src/test/java/org/apache/gravitino/server/web/TestUtils.java
@@ -116,6 +116,16 @@ public class TestUtils {
     assertEquals("Connection failed", errorResponse.getMessage());
   }
 
+  @Test
+  public void testServiceUnavailable() {
+    Response response = Utils.serviceUnavailable("Service unavailable");
+    assertNotNull(response);
+    assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), 
response.getStatus());
+    assertEquals(MediaType.APPLICATION_JSON, 
response.getMediaType().toString());
+    ErrorResponse errorResponse = (ErrorResponse) response.getEntity();
+    assertEquals("Service unavailable", errorResponse.getMessage());
+  }
+
   @Test
   public void testInternalError() {
     Response response = Utils.internalError("Internal error");
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
index a9eeaad49f..80ee553cd5 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/ExceptionHandlers.java
@@ -50,6 +50,7 @@ import 
org.apache.gravitino.exceptions.PolicyAlreadyExistsException;
 import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
 import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
 import org.apache.gravitino.exceptions.SemanticModelAlreadyExistsException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
 import org.apache.gravitino.exceptions.TableAlreadyExistsException;
 import org.apache.gravitino.exceptions.TagAlreadyAssociatedException;
 import org.apache.gravitino.exceptions.TagAlreadyExistsException;
@@ -227,6 +228,9 @@ public class ExceptionHandlers {
     if (e instanceof IllegalArgumentException) {
       response = ErrorResponse.illegalArguments(e.getMessage(), throwable);
 
+    } else if (e instanceof ServiceUnavailableException) {
+      response = ErrorResponse.serviceUnavailable(e.getMessage(), throwable);
+
     } else if (e instanceof ConnectionFailedException) {
       response = ErrorResponse.connectionFailed(e.getMessage(), throwable);
 
@@ -435,13 +439,19 @@ public class ExceptionHandlers {
     public Response handle(OperationType op, String catalog, String metalake, 
Exception e) {
       String formatted = StringUtil.isBlank(catalog) ? "" : " [" + catalog + 
"]";
       // Keep schema-check context even when the backend message contains 
"Exception: ".
-      String reason = e instanceof ConnectionFailedException ? e.getMessage() 
: getErrorMsg(e);
+      String reason =
+          e instanceof ConnectionFailedException || e instanceof 
ServiceUnavailableException
+              ? e.getMessage()
+              : getErrorMsg(e);
       String errorMsg = getCatalogErrorMsg(formatted, op.name(), metalake, 
reason);
       LOG.warn(errorMsg, e);
 
       if (e instanceof IllegalArgumentException) {
         return Utils.illegalArguments(errorMsg, e);
 
+      } else if (e instanceof ServiceUnavailableException) {
+        return Utils.serviceUnavailable(errorMsg, e);
+
       } else if (e instanceof ConnectionFailedException) {
         return Utils.connectionFailed(errorMsg, e);
 
@@ -805,12 +815,16 @@ public class ExceptionHandlers {
 
     @Override
     public Response handle(OperationType op, String secret, String parent, 
Exception e) {
-      String errorMsg = getSecretErrorMsg(parent, getErrorMsg(e));
+      String reason = e instanceof ServiceUnavailableException ? 
e.getMessage() : getErrorMsg(e);
+      String errorMsg = getSecretErrorMsg(parent, reason);
       LOG.warn(errorMsg, e);
 
       if (e instanceof IllegalArgumentException) {
         return Utils.illegalArguments(errorMsg, e);
 
+      } else if (e instanceof ServiceUnavailableException) {
+        return Utils.serviceUnavailable(errorMsg, e);
+
       } else if (e instanceof NotFoundException) {
         return Utils.notFound(errorMsg, e);
 
@@ -1042,6 +1056,9 @@ public class ExceptionHandlers {
       } else if (e instanceof UnsupportedOperationException) {
         return Utils.unsupportedOperation(errorMsg, e);
 
+      } else if (e instanceof ServiceUnavailableException) {
+        return Utils.serviceUnavailable(errorMsg, e);
+
       } else if (e instanceof ConnectionFailedException) {
         return Utils.connectionFailed(errorMsg, e);
 
@@ -1229,6 +1246,14 @@ public class ExceptionHandlers {
       String errorMsg =
           getBaseErrorMsg(formattedObject, op.name(), formattedParent, 
getErrorMsg(e));
 
+      // A configured dependency that is temporarily unavailable is not an 
internal Gravitino error:
+      // surface it as 503 Service Unavailable so callers can tell an outage 
from a server bug.
+      if (e instanceof ServiceUnavailableException) {
+        // WARN, not ERROR: a dependency outage is not a Gravitino bug, but 
still trace it here.
+        LOG.warn(errorMsg, e);
+        return Utils.serviceUnavailable(errorMsg, e);
+      }
+
       // A backend a catalog federates to being unreachable is a 
downstream-dependency failure, not
       // an internal Gravitino error: surface it as 502 Bad Gateway so callers 
can tell a dependency
       // outage from a server bug.
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
index afbc556a9f..44fec2564f 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestExceptionHandlers.java
@@ -26,6 +26,7 @@ import org.apache.gravitino.exceptions.InUseException;
 import org.apache.gravitino.exceptions.NoSuchJobTemplateException;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.OptimisticLockException;
+import org.apache.gravitino.exceptions.ServiceUnavailableException;
 import org.apache.gravitino.exceptions.UnmodifiableStatisticException;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -132,6 +133,23 @@ public class TestExceptionHandlers {
     Assertions.assertEquals(OptimisticLockException.class.getSimpleName(), 
errorResponse.getType());
   }
 
+  @Test
+  public void testServiceUnavailableReturnsServiceUnavailable() {
+    Response response =
+        ExceptionHandlers.handleTableException(
+            OperationType.LOAD,
+            "table",
+            "schema",
+            new ServiceUnavailableException("secret provider 'vault' is 
unavailable"));
+
+    Assertions.assertEquals(
+        Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), 
response.getStatus());
+    ErrorResponse errorResponse = (ErrorResponse) response.getEntity();
+    Assertions.assertEquals(ErrorConstants.SERVICE_UNAVAILABLE_CODE, 
errorResponse.getCode());
+    Assertions.assertEquals(
+        ServiceUnavailableException.class.getSimpleName(), 
errorResponse.getType());
+  }
+
   @Test
   void testUnsupportedOperationReturnsNotImplemented() {
     UnsupportedOperationException exception =

Reply via email to