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

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


The following commit(s) were added to refs/heads/main by this push:
     new eca6e70f When a grant already exists, return the appropriate response 
(#243)
eca6e70f is described below

commit eca6e70f1a84065c1a647b4bc94ec0524b5d774c
Author: Eric Maynard <[email protected]>
AuthorDate: Thu Oct 3 16:13:58 2024 -0400

    When a grant already exists, return the appropriate response (#243)
---
 .../PolarisEclipseLinkMetaStoreSessionImpl.java    |  9 ++++++
 .../core/exceptions/AlreadyExistsException.java    | 33 ++++++++++++++++++++
 .../polaris/core/exceptions/PolarisException.java  | 35 ++++++++++++++++++++++
 .../apache/polaris/service/PolarisApplication.java |  5 ++++
 .../{ => exception}/IcebergExceptionMapper.java    |  2 +-
 .../IcebergJerseyViolationExceptionMapper.java     |  2 +-
 .../IcebergJsonProcessingExceptionMapper.java      |  2 +-
 .../PolarisExceptionMapper.java}                   | 35 ++++++++++++++--------
 8 files changed, 107 insertions(+), 16 deletions(-)

diff --git 
a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java
 
b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java
index 75d4530d..071d7fbc 100644
--- 
a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java
+++ 
b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java
@@ -27,6 +27,7 @@ import jakarta.persistence.EntityManagerFactory;
 import jakarta.persistence.EntityTransaction;
 import jakarta.persistence.OptimisticLockException;
 import jakarta.persistence.Persistence;
+import jakarta.persistence.PersistenceException;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -34,6 +35,7 @@ import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
@@ -58,6 +60,7 @@ import org.apache.polaris.core.entity.PolarisEntityId;
 import org.apache.polaris.core.entity.PolarisEntityType;
 import org.apache.polaris.core.entity.PolarisGrantRecord;
 import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
+import org.apache.polaris.core.exceptions.AlreadyExistsException;
 import org.apache.polaris.core.persistence.PolarisMetaStoreManagerImpl;
 import org.apache.polaris.core.persistence.PolarisMetaStoreSession;
 import org.apache.polaris.core.persistence.RetryOnConcurrencyException;
@@ -261,6 +264,12 @@ public class PolarisEclipseLinkMetaStoreSessionImpl 
implements PolarisMetaStoreS
       } finally {
         localSession.remove();
       }
+    } catch (PersistenceException e) {
+      if (e.toString().toLowerCase(Locale.ROOT).contains("duplicate key")) {
+        throw new AlreadyExistsException("Duplicate key error when persisting 
entity", e);
+      } else {
+        throw e;
+      }
     }
   }
 
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/exceptions/AlreadyExistsException.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/exceptions/AlreadyExistsException.java
new file mode 100644
index 00000000..a941a549
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/exceptions/AlreadyExistsException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.polaris.core.exceptions;
+
+/**
+ * A {@link PolarisException} implementation for when Polaris is unable to 
create an entity that
+ * already exists.
+ */
+public class AlreadyExistsException extends PolarisException {
+  public AlreadyExistsException(String message) {
+    super(message);
+  }
+
+  public AlreadyExistsException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/exceptions/PolarisException.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/exceptions/PolarisException.java
new file mode 100644
index 00000000..96ef9cf7
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/exceptions/PolarisException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.polaris.core.exceptions;
+
+/**
+ * Base class for Polaris-specific runtime exceptions.
+ *
+ * <p>All custom exceptions in Polaris should extend this class to provide 
specific error details.
+ */
+public abstract class PolarisException extends RuntimeException {
+
+  public PolarisException(String message) {
+    super(message);
+  }
+
+  public PolarisException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git 
a/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
 
b/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
index e62e8712..c782c6a9 100644
--- 
a/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
+++ 
b/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
@@ -100,6 +100,10 @@ import 
org.apache.polaris.service.context.CallContextCatalogFactory;
 import org.apache.polaris.service.context.CallContextResolver;
 import org.apache.polaris.service.context.PolarisCallContextCatalogFactory;
 import org.apache.polaris.service.context.RealmContextResolver;
+import org.apache.polaris.service.exception.IcebergExceptionMapper;
+import 
org.apache.polaris.service.exception.IcebergJerseyViolationExceptionMapper;
+import 
org.apache.polaris.service.exception.IcebergJsonProcessingExceptionMapper;
+import org.apache.polaris.service.exception.PolarisExceptionMapper;
 import 
org.apache.polaris.service.persistence.InMemoryPolarisMetaStoreManagerFactory;
 import org.apache.polaris.service.ratelimiter.RateLimiterFilter;
 import 
org.apache.polaris.service.storage.PolarisStorageIntegrationProviderImpl;
@@ -277,6 +281,7 @@ public class PolarisApplication extends 
Application<PolarisApplicationConfig> {
     }
     environment.jersey().register(new IcebergRestOAuth2Api(oauth2Service));
     environment.jersey().register(new IcebergExceptionMapper());
+    environment.jersey().register(new PolarisExceptionMapper());
     PolarisServiceImpl polarisService = new 
PolarisServiceImpl(entityManagerFactory, authorizer);
     environment.jersey().register(new PolarisCatalogsApi(polarisService));
     environment.jersey().register(new PolarisPrincipalsApi(polarisService));
diff --git 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergExceptionMapper.java
 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergExceptionMapper.java
similarity index 99%
rename from 
polaris-service/src/main/java/org/apache/polaris/service/IcebergExceptionMapper.java
rename to 
polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergExceptionMapper.java
index b829a39c..565b1a70 100644
--- 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergExceptionMapper.java
+++ 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergExceptionMapper.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.service;
+package org.apache.polaris.service.exception;
 
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.MediaType;
diff --git 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergJerseyViolationExceptionMapper.java
 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergJerseyViolationExceptionMapper.java
similarity index 97%
copy from 
polaris-service/src/main/java/org/apache/polaris/service/IcebergJerseyViolationExceptionMapper.java
copy to 
polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergJerseyViolationExceptionMapper.java
index 2ffbdbbb..e26e89a2 100644
--- 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergJerseyViolationExceptionMapper.java
+++ 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergJerseyViolationExceptionMapper.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.service;
+package org.apache.polaris.service.exception;
 
 import io.dropwizard.jersey.validation.JerseyViolationException;
 import jakarta.ws.rs.core.MediaType;
diff --git 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergJsonProcessingExceptionMapper.java
 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergJsonProcessingExceptionMapper.java
similarity index 98%
rename from 
polaris-service/src/main/java/org/apache/polaris/service/IcebergJsonProcessingExceptionMapper.java
rename to 
polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergJsonProcessingExceptionMapper.java
index 72992008..929453ae 100644
--- 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergJsonProcessingExceptionMapper.java
+++ 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/IcebergJsonProcessingExceptionMapper.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.service;
+package org.apache.polaris.service.exception;
 
 import com.fasterxml.jackson.core.JsonGenerationException;
 import com.fasterxml.jackson.core.JsonParseException;
diff --git 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergJerseyViolationExceptionMapper.java
 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
similarity index 57%
rename from 
polaris-service/src/main/java/org/apache/polaris/service/IcebergJerseyViolationExceptionMapper.java
rename to 
polaris-service/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
index 2ffbdbbb..489b3735 100644
--- 
a/polaris-service/src/main/java/org/apache/polaris/service/IcebergJerseyViolationExceptionMapper.java
+++ 
b/polaris-service/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java
@@ -16,34 +16,43 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.service;
+package org.apache.polaris.service.exception;
 
-import io.dropwizard.jersey.validation.JerseyViolationException;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.ext.ExceptionMapper;
 import jakarta.ws.rs.ext.Provider;
 import org.apache.iceberg.rest.responses.ErrorResponse;
+import org.apache.polaris.core.exceptions.AlreadyExistsException;
+import org.apache.polaris.core.exceptions.PolarisException;
 
 /**
- * Override of the default JerseyViolationExceptionMapper to provide an 
Iceberg ErrorResponse with
- * the exception details.
+ * An {@link ExceptionMapper} implementation for {@link PolarisException}s 
modeled after {@link
+ * IcebergExceptionMapper}
  */
 @Provider
-public class IcebergJerseyViolationExceptionMapper
-    implements ExceptionMapper<JerseyViolationException> {
+public class PolarisExceptionMapper implements 
ExceptionMapper<PolarisException> {
+
+  private Response.Status getStatus(PolarisException exception) {
+    if (exception instanceof AlreadyExistsException) {
+      return Response.Status.CONFLICT;
+    } else {
+      return Response.Status.INTERNAL_SERVER_ERROR;
+    }
+  }
+
   @Override
-  public Response toResponse(JerseyViolationException exception) {
-    final String message = "Invalid value: " + exception.getMessage();
-    ErrorResponse icebergErrorResponse =
+  public Response toResponse(PolarisException exception) {
+    Response.Status status = getStatus(exception);
+    ErrorResponse errorResponse =
         ErrorResponse.builder()
-            .responseCode(Response.Status.BAD_REQUEST.getStatusCode())
+            .responseCode(status.getStatusCode())
             .withType(exception.getClass().getSimpleName())
-            .withMessage(message)
+            .withMessage(exception.getMessage())
             .build();
-    return Response.status(Response.Status.BAD_REQUEST)
+    return Response.status(status)
+        .entity(errorResponse)
         .type(MediaType.APPLICATION_JSON_TYPE)
-        .entity(icebergErrorResponse)
         .build();
   }
 }

Reply via email to