Skip to content

Commit

Permalink
When a grant already exists, return the appropriate response (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-maynard authored Oct 3, 2024
1 parent 6f66848 commit eca6e70
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@
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;
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;
Expand All @@ -58,6 +60,7 @@
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;
Expand Down Expand Up @@ -261,6 +264,12 @@ public <T> T runInTransaction(
} 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;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
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;
Expand Down Expand Up @@ -277,6 +281,7 @@ public void run(PolarisApplicationConfig configuration, Environment environment)
}
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.service.exception;

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;

/**
* An {@link ExceptionMapper} implementation for {@link PolarisException}s modeled after {@link
* IcebergExceptionMapper}
*/
@Provider
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(PolarisException exception) {
Response.Status status = getStatus(exception);
ErrorResponse errorResponse =
ErrorResponse.builder()
.responseCode(status.getStatusCode())
.withType(exception.getClass().getSimpleName())
.withMessage(exception.getMessage())
.build();
return Response.status(status)
.entity(errorResponse)
.type(MediaType.APPLICATION_JSON_TYPE)
.build();
}
}

0 comments on commit eca6e70

Please sign in to comment.