You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polaris.apache.org by di...@apache.org on 2026/07/08 19:16:27 UTC
(polaris) branch main updated: Add catalog config endpoint registry (#4926)
This is an automated email from the ASF dual-hosted git repository.
dimas-b 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 ec207753b5 Add catalog config endpoint registry (#4926)
ec207753b5 is described below
commit ec207753b53617f4b259894c53b36332fbfd3c60
Author: Adam Christian <10...@users.noreply.github.com>
AuthorDate: Wed Jul 8 15:16:23 2026 -0400
Add catalog config endpoint registry (#4926)
* Add Polaris catalog config discovery endpoint
* Remove Polaris catalog config endpoint
* Inject RealmConfig into catalog config handler
* Share generic table endpoints with Spark
* Document generic table endpoint placement
* Stabilize catalog config endpoint order
---
extensions/semantic-models/build.gradle.kts | 4 +
.../SemanticModelConfigEndpoints.java | 57 ++++++++++
.../semanticmodel/SemanticModelEndpoints.java | 48 ++++++++
.../SemanticModelConfigEndpointsTest.java | 52 +++++++++
.../apache/polaris/spark/PolarisRESTCatalog.java | 13 ++-
.../polaris/spark/PolarisRESTCatalogTest.java | 14 +--
.../rest/CatalogConfigEndpointContributor.java | 32 ++++++
.../polaris/core/rest/GenericTableEndpoints.java | 47 ++++++++
.../apache/polaris/core/rest/PolarisEndpoints.java | 126 ---------------------
.../catalog/config/CatalogConfigHandler.java | 104 +++++++++++++++++
.../generic/GenericTableConfigEndpoints.java | 55 +++++++++
.../catalog/iceberg/IcebergCatalogAdapter.java | 10 +-
.../catalog/iceberg/IcebergCatalogHandler.java | 75 ------------
.../iceberg/IcebergRestConfigEndpoints.java | 55 +++++++++
.../iceberg/IcebergViewConfigEndpoints.java | 47 ++++++++
.../catalog/policy/PolicyConfigEndpoints.java | 54 +++++++++
.../service/catalog/policy/PolicyEndpoints.java | 57 ++++++++++
.../service/catalog/iceberg/GetConfigTest.java | 49 ++++++--
.../org/apache/polaris/service/TestServices.java | 55 ++++++++-
19 files changed, 727 insertions(+), 227 deletions(-)
diff --git a/extensions/semantic-models/build.gradle.kts b/extensions/semantic-models/build.gradle.kts
index 2644a4ed57..ff03fd9da0 100644
--- a/extensions/semantic-models/build.gradle.kts
+++ b/extensions/semantic-models/build.gradle.kts
@@ -69,6 +69,10 @@ dependencies {
compileOnly(project(":polaris-immutables"))
annotationProcessor(project(":polaris-immutables", configuration = "processor"))
+
+ testImplementation(platform(libs.junit.bom))
+ testImplementation("org.junit.jupiter:junit-jupiter")
+ testImplementation(libs.assertj.core)
}
val rootDir = rootProject.layout.projectDirectory
diff --git a/extensions/semantic-models/src/main/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelConfigEndpoints.java b/extensions/semantic-models/src/main/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelConfigEndpoints.java
new file mode 100644
index 0000000000..8922a637e0
--- /dev/null
+++ b/extensions/semantic-models/src/main/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelConfigEndpoints.java
@@ -0,0 +1,57 @@
+/*
+ * 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.catalog.semanticmodel;
+
+import com.google.common.collect.ImmutableSet;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.config.FeatureConfiguration;
+import org.apache.polaris.core.config.RealmConfig;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
+
+@ApplicationScoped
+@Priority(500)
+public class SemanticModelConfigEndpoints implements CatalogConfigEndpointContributor {
+ private final RealmConfig realmConfig;
+
+ @Inject
+ public SemanticModelConfigEndpoints(RealmConfig realmConfig) {
+ this.realmConfig = realmConfig;
+ }
+
+ @Override
+ public Set<Endpoint> endpoints() {
+ return getSupportedSemanticModelEndpoints(realmConfig);
+ }
+
+ /**
+ * Get the semantic model endpoints. Returns SEMANTIC_MODEL_ENDPOINTS if ENABLE_SEMANTIC_MODELS is
+ * set to true, otherwise, returns an empty set.
+ */
+ public static Set<Endpoint> getSupportedSemanticModelEndpoints(RealmConfig realmConfig) {
+ boolean semanticModelsEnabled =
+ realmConfig.getConfig(FeatureConfiguration.ENABLE_SEMANTIC_MODELS);
+ return semanticModelsEnabled
+ ? SemanticModelEndpoints.SEMANTIC_MODEL_ENDPOINTS
+ : ImmutableSet.of();
+ }
+}
diff --git a/extensions/semantic-models/src/main/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelEndpoints.java b/extensions/semantic-models/src/main/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelEndpoints.java
new file mode 100644
index 0000000000..f12e8eed97
--- /dev/null
+++ b/extensions/semantic-models/src/main/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelEndpoints.java
@@ -0,0 +1,48 @@
+/*
+ * 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.catalog.semanticmodel;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.rest.PolarisResourcePaths;
+
+public class SemanticModelEndpoints {
+ private SemanticModelEndpoints() {}
+
+ public static final Endpoint V1_LIST_SEMANTIC_MODELS =
+ Endpoint.create("GET", PolarisResourcePaths.V1_SEMANTIC_MODELS);
+ public static final Endpoint V1_CREATE_SEMANTIC_MODEL =
+ Endpoint.create("POST", PolarisResourcePaths.V1_SEMANTIC_MODELS);
+ public static final Endpoint V1_LOAD_SEMANTIC_MODEL =
+ Endpoint.create("GET", PolarisResourcePaths.V1_SEMANTIC_MODEL);
+ public static final Endpoint V1_UPDATE_SEMANTIC_MODEL =
+ Endpoint.create("PUT", PolarisResourcePaths.V1_SEMANTIC_MODEL);
+ public static final Endpoint V1_DROP_SEMANTIC_MODEL =
+ Endpoint.create("DELETE", PolarisResourcePaths.V1_SEMANTIC_MODEL);
+
+ public static final Set<Endpoint> SEMANTIC_MODEL_ENDPOINTS =
+ ImmutableSet.<Endpoint>builder()
+ .add(V1_LIST_SEMANTIC_MODELS)
+ .add(V1_CREATE_SEMANTIC_MODEL)
+ .add(V1_LOAD_SEMANTIC_MODEL)
+ .add(V1_UPDATE_SEMANTIC_MODEL)
+ .add(V1_DROP_SEMANTIC_MODEL)
+ .build();
+}
diff --git a/extensions/semantic-models/src/test/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelConfigEndpointsTest.java b/extensions/semantic-models/src/test/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelConfigEndpointsTest.java
new file mode 100644
index 0000000000..12a887dc9b
--- /dev/null
+++ b/extensions/semantic-models/src/test/java/org/apache/polaris/service/catalog/semanticmodel/SemanticModelConfigEndpointsTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.catalog.semanticmodel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.polaris.core.config.FeatureConfiguration;
+import org.apache.polaris.core.config.RealmConfig;
+import org.apache.polaris.core.config.RealmConfigImpl;
+import org.junit.jupiter.api.Test;
+
+public class SemanticModelConfigEndpointsTest {
+ @Test
+ public void endpointsIncludeSemanticModelsWhenFeatureEnabled() {
+ SemanticModelConfigEndpoints endpoints = new SemanticModelConfigEndpoints(realmConfig(true));
+
+ assertThat(endpoints.endpoints())
+ .containsExactlyInAnyOrderElementsOf(SemanticModelEndpoints.SEMANTIC_MODEL_ENDPOINTS);
+ }
+
+ @Test
+ public void endpointsEmptyWhenFeatureDisabled() {
+ SemanticModelConfigEndpoints endpoints = new SemanticModelConfigEndpoints(realmConfig(false));
+
+ assertThat(endpoints.endpoints()).isEmpty();
+ }
+
+ private static RealmConfig realmConfig(boolean enableSemanticModels) {
+ return new RealmConfigImpl(
+ (realmContext, configName) ->
+ FeatureConfiguration.ENABLE_SEMANTIC_MODELS.key().equals(configName)
+ ? enableSemanticModels
+ : null,
+ () -> "test-realm");
+ }
+}
diff --git a/plugins/spark/common/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java b/plugins/spark/common/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java
index 5be0f6952e..36496b3fd8 100644
--- a/plugins/spark/common/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java
+++ b/plugins/spark/common/src/main/java/org/apache/polaris/spark/PolarisRESTCatalog.java
@@ -44,7 +44,7 @@ import org.apache.iceberg.rest.auth.OAuth2Util;
import org.apache.iceberg.rest.responses.ConfigResponse;
import org.apache.iceberg.util.EnvironmentUtil;
import org.apache.iceberg.util.PropertyUtil;
-import org.apache.polaris.core.rest.PolarisEndpoints;
+import org.apache.polaris.core.rest.GenericTableEndpoints;
import org.apache.polaris.core.rest.PolarisResourcePaths;
import org.apache.polaris.spark.rest.CreateGenericTableRESTRequest;
import org.apache.polaris.spark.rest.CreateGenericTableRequest;
@@ -70,7 +70,8 @@ public class PolarisRESTCatalog implements PolarisCatalog, Closeable {
private Integer pageSize = null;
// the default endpoints to config if server doesn't specify the 'endpoints' configuration.
- private static final Set<Endpoint> DEFAULT_ENDPOINTS = PolarisEndpoints.GENERIC_TABLE_ENDPOINTS;
+ private static final Set<Endpoint> DEFAULT_ENDPOINTS =
+ GenericTableEndpoints.GENERIC_TABLE_ENDPOINTS;
public PolarisRESTCatalog() {
this(config -> HTTPClient.builder(config).uri(config.get(CatalogProperties.URI)).build());
@@ -153,7 +154,7 @@ public class PolarisRESTCatalog implements PolarisCatalog, Closeable {
@Override
public List<TableIdentifier> listGenericTables(Namespace ns) {
- Endpoint.check(endpoints, PolarisEndpoints.V1_LIST_GENERIC_TABLES);
+ Endpoint.check(endpoints, GenericTableEndpoints.V1_LIST_GENERIC_TABLES);
Map<String, String> queryParams = Maps.newHashMap();
ImmutableList.Builder<TableIdentifier> tables = ImmutableList.builder();
@@ -182,7 +183,7 @@ public class PolarisRESTCatalog implements PolarisCatalog, Closeable {
@Override
public boolean dropGenericTable(TableIdentifier identifier) {
- Endpoint.check(endpoints, PolarisEndpoints.V1_DELETE_GENERIC_TABLE);
+ Endpoint.check(endpoints, GenericTableEndpoints.V1_DELETE_GENERIC_TABLE);
try {
restClient
@@ -205,7 +206,7 @@ public class PolarisRESTCatalog implements PolarisCatalog, Closeable {
String baseLocation,
String doc,
Map<String, String> props) {
- Endpoint.check(endpoints, PolarisEndpoints.V1_CREATE_GENERIC_TABLE);
+ Endpoint.check(endpoints, GenericTableEndpoints.V1_CREATE_GENERIC_TABLE);
CreateGenericTableRESTRequest request =
new CreateGenericTableRESTRequest(
CreateGenericTableRequest.builder()
@@ -231,7 +232,7 @@ public class PolarisRESTCatalog implements PolarisCatalog, Closeable {
@Override
public GenericTable loadGenericTable(TableIdentifier identifier) {
- Endpoint.check(endpoints, PolarisEndpoints.V1_LOAD_GENERIC_TABLE);
+ Endpoint.check(endpoints, GenericTableEndpoints.V1_LOAD_GENERIC_TABLE);
LoadGenericTableRESTResponse response =
restClient
.withAuthSession(this.catalogAuth)
diff --git a/plugins/spark/common/src/test/java/org/apache/polaris/spark/PolarisRESTCatalogTest.java b/plugins/spark/common/src/test/java/org/apache/polaris/spark/PolarisRESTCatalogTest.java
index a5af3498c9..4582c36cee 100644
--- a/plugins/spark/common/src/test/java/org/apache/polaris/spark/PolarisRESTCatalogTest.java
+++ b/plugins/spark/common/src/test/java/org/apache/polaris/spark/PolarisRESTCatalogTest.java
@@ -39,7 +39,7 @@ import org.apache.iceberg.exceptions.NoSuchTableException;
import org.apache.iceberg.rest.RESTClient;
import org.apache.iceberg.rest.auth.OAuth2Util;
import org.apache.iceberg.rest.responses.ConfigResponse;
-import org.apache.polaris.core.rest.PolarisEndpoints;
+import org.apache.polaris.core.rest.GenericTableEndpoints;
import org.apache.polaris.spark.rest.GenericTable;
import org.apache.polaris.spark.rest.ListGenericTablesRESTResponse;
import org.apache.polaris.spark.rest.LoadGenericTableRESTResponse;
@@ -90,8 +90,8 @@ public class PolarisRESTCatalogTest {
.withOverrides(ImmutableMap.of())
.withEndpoints(
ImmutableList.of(
- PolarisEndpoints.V1_LIST_GENERIC_TABLES,
- PolarisEndpoints.V1_CREATE_GENERIC_TABLE))
+ GenericTableEndpoints.V1_LIST_GENERIC_TABLES,
+ GenericTableEndpoints.V1_CREATE_GENERIC_TABLE))
.build();
when(mockClient.get(any(), anyMap(), eq(ConfigResponse.class), anyMap(), any()))
@@ -328,10 +328,10 @@ public class PolarisRESTCatalogTest {
.withOverrides(ImmutableMap.of())
.withEndpoints(
ImmutableList.of(
- PolarisEndpoints.V1_LIST_GENERIC_TABLES,
- PolarisEndpoints.V1_CREATE_GENERIC_TABLE,
- PolarisEndpoints.V1_LOAD_GENERIC_TABLE,
- PolarisEndpoints.V1_DELETE_GENERIC_TABLE))
+ GenericTableEndpoints.V1_LIST_GENERIC_TABLES,
+ GenericTableEndpoints.V1_CREATE_GENERIC_TABLE,
+ GenericTableEndpoints.V1_LOAD_GENERIC_TABLE,
+ GenericTableEndpoints.V1_DELETE_GENERIC_TABLE))
.build();
when(mockClient.get(any(), anyMap(), eq(ConfigResponse.class), anyMap(), any()))
diff --git a/polaris-core/src/main/java/org/apache/polaris/core/rest/CatalogConfigEndpointContributor.java b/polaris-core/src/main/java/org/apache/polaris/core/rest/CatalogConfigEndpointContributor.java
new file mode 100644
index 0000000000..4e93f2656e
--- /dev/null
+++ b/polaris-core/src/main/java/org/apache/polaris/core/rest/CatalogConfigEndpointContributor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rest;
+
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+
+/**
+ * Contributes endpoints advertised in catalog configuration responses.
+ *
+ * <p>Implementations are discovered through CDI as {@code CatalogConfigEndpointContributor} beans.
+ */
+@FunctionalInterface
+public interface CatalogConfigEndpointContributor {
+ Set<Endpoint> endpoints();
+}
diff --git a/polaris-core/src/main/java/org/apache/polaris/core/rest/GenericTableEndpoints.java b/polaris-core/src/main/java/org/apache/polaris/core/rest/GenericTableEndpoints.java
new file mode 100644
index 0000000000..27c6cda35f
--- /dev/null
+++ b/polaris-core/src/main/java/org/apache/polaris/core/rest/GenericTableEndpoints.java
@@ -0,0 +1,47 @@
+/*
+ * 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.rest;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+
+// TODO: This is a temporary hack to share generic-table endpoint constants with clients.
+// Endpoint definitions should not be exposed from polaris-core; move these to a dedicated
+// shared API module when one exists.
+public class GenericTableEndpoints {
+ private GenericTableEndpoints() {}
+
+ public static final Endpoint V1_LIST_GENERIC_TABLES =
+ Endpoint.create("GET", PolarisResourcePaths.V1_GENERIC_TABLES);
+ public static final Endpoint V1_LOAD_GENERIC_TABLE =
+ Endpoint.create("GET", PolarisResourcePaths.V1_GENERIC_TABLE);
+ public static final Endpoint V1_CREATE_GENERIC_TABLE =
+ Endpoint.create("POST", PolarisResourcePaths.V1_GENERIC_TABLES);
+ public static final Endpoint V1_DELETE_GENERIC_TABLE =
+ Endpoint.create("DELETE", PolarisResourcePaths.V1_GENERIC_TABLE);
+
+ public static final Set<Endpoint> GENERIC_TABLE_ENDPOINTS =
+ ImmutableSet.<Endpoint>builder()
+ .add(V1_LIST_GENERIC_TABLES)
+ .add(V1_CREATE_GENERIC_TABLE)
+ .add(V1_DELETE_GENERIC_TABLE)
+ .add(V1_LOAD_GENERIC_TABLE)
+ .build();
+}
diff --git a/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisEndpoints.java b/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisEndpoints.java
deleted file mode 100644
index 8f2b8a3812..0000000000
--- a/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisEndpoints.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.rest;
-
-import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import org.apache.iceberg.rest.Endpoint;
-import org.apache.polaris.core.config.FeatureConfiguration;
-import org.apache.polaris.core.config.RealmConfig;
-
-public class PolarisEndpoints {
- // Generic table endpoints
- public static final Endpoint V1_LIST_GENERIC_TABLES =
- Endpoint.create("GET", PolarisResourcePaths.V1_GENERIC_TABLES);
- public static final Endpoint V1_LOAD_GENERIC_TABLE =
- Endpoint.create("GET", PolarisResourcePaths.V1_GENERIC_TABLE);
- public static final Endpoint V1_CREATE_GENERIC_TABLE =
- Endpoint.create("POST", PolarisResourcePaths.V1_GENERIC_TABLES);
- public static final Endpoint V1_DELETE_GENERIC_TABLE =
- Endpoint.create("DELETE", PolarisResourcePaths.V1_GENERIC_TABLE);
-
- public static final Set<Endpoint> GENERIC_TABLE_ENDPOINTS =
- ImmutableSet.<Endpoint>builder()
- .add(V1_LIST_GENERIC_TABLES)
- .add(V1_CREATE_GENERIC_TABLE)
- .add(V1_DELETE_GENERIC_TABLE)
- .add(V1_LOAD_GENERIC_TABLE)
- .build();
-
- // Policy store endpoints
- public static final Endpoint V1_LIST_POLICIES =
- Endpoint.create("GET", PolarisResourcePaths.V1_POLICIES);
- public static final Endpoint V1_CREATE_POLICY =
- Endpoint.create("POST", PolarisResourcePaths.V1_POLICIES);
- public static final Endpoint V1_LOAD_POLICY =
- Endpoint.create("GET", PolarisResourcePaths.V1_POLICY);
- public static final Endpoint V1_UPDATE_POLICY =
- Endpoint.create("PUT", PolarisResourcePaths.V1_POLICY);
- public static final Endpoint V1_DROP_POLICY =
- Endpoint.create("DELETE", PolarisResourcePaths.V1_POLICY);
- public static final Endpoint V1_ATTACH_POLICY =
- Endpoint.create("PUT", PolarisResourcePaths.V1_POLICY_MAPPINGS);
- public static final Endpoint V1_DETACH_POLICY =
- Endpoint.create("POST", PolarisResourcePaths.V1_POLICY_MAPPINGS);
- public static final Endpoint V1_GET_APPLICABLE_POLICIES =
- Endpoint.create("GET", PolarisResourcePaths.V1_APPLICABLE_POLICIES);
-
- public static final Set<Endpoint> POLICY_STORE_ENDPOINTS =
- ImmutableSet.<Endpoint>builder()
- .add(V1_LIST_POLICIES)
- .add(V1_CREATE_POLICY)
- .add(V1_LOAD_POLICY)
- .add(V1_UPDATE_POLICY)
- .add(V1_DROP_POLICY)
- .add(V1_ATTACH_POLICY)
- .add(V1_DETACH_POLICY)
- .add(V1_GET_APPLICABLE_POLICIES)
- .build();
-
- // Semantic model endpoints
- public static final Endpoint V1_LIST_SEMANTIC_MODELS =
- Endpoint.create("GET", PolarisResourcePaths.V1_SEMANTIC_MODELS);
- public static final Endpoint V1_CREATE_SEMANTIC_MODEL =
- Endpoint.create("POST", PolarisResourcePaths.V1_SEMANTIC_MODELS);
- public static final Endpoint V1_LOAD_SEMANTIC_MODEL =
- Endpoint.create("GET", PolarisResourcePaths.V1_SEMANTIC_MODEL);
- public static final Endpoint V1_UPDATE_SEMANTIC_MODEL =
- Endpoint.create("PUT", PolarisResourcePaths.V1_SEMANTIC_MODEL);
- public static final Endpoint V1_DROP_SEMANTIC_MODEL =
- Endpoint.create("DELETE", PolarisResourcePaths.V1_SEMANTIC_MODEL);
-
- public static final Set<Endpoint> SEMANTIC_MODEL_ENDPOINTS =
- ImmutableSet.<Endpoint>builder()
- .add(V1_LIST_SEMANTIC_MODELS)
- .add(V1_CREATE_SEMANTIC_MODEL)
- .add(V1_LOAD_SEMANTIC_MODEL)
- .add(V1_UPDATE_SEMANTIC_MODEL)
- .add(V1_DROP_SEMANTIC_MODEL)
- .build();
-
- /**
- * Get the generic table endpoints. Returns GENERIC_TABLE_ENDPOINTS if ENABLE_GENERIC_TABLES is
- * set to true, otherwise, returns an empty set.
- */
- public static Set<Endpoint> getSupportedGenericTableEndpoints(RealmConfig realmConfig) {
- // add the generic table endpoints as supported endpoints if generic table feature is enabled.
- boolean genericTableEnabled = realmConfig.getConfig(FeatureConfiguration.ENABLE_GENERIC_TABLES);
-
- return genericTableEnabled ? GENERIC_TABLE_ENDPOINTS : ImmutableSet.of();
- }
-
- /**
- * Get the policy store endpoints. Returns POLICY_ENDPOINTS if ENABLE_POLICY_STORE is set to true,
- * otherwise, returns an empty set
- */
- public static Set<Endpoint> getSupportedPolicyEndpoints(RealmConfig realmConfig) {
- boolean policyStoreEnabled = realmConfig.getConfig(FeatureConfiguration.ENABLE_POLICY_STORE);
- return policyStoreEnabled ? POLICY_STORE_ENDPOINTS : ImmutableSet.of();
- }
-
- /**
- * Get the semantic model endpoints. Returns SEMANTIC_MODEL_ENDPOINTS if ENABLE_SEMANTIC_MODELS is
- * set to true, otherwise, returns an empty set.
- */
- public static Set<Endpoint> getSupportedSemanticModelEndpoints(RealmConfig realmConfig) {
- boolean semanticModelsEnabled =
- realmConfig.getConfig(FeatureConfiguration.ENABLE_SEMANTIC_MODELS);
- return semanticModelsEnabled ? SEMANTIC_MODEL_ENDPOINTS : ImmutableSet.of();
- }
-}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/config/CatalogConfigHandler.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/config/CatalogConfigHandler.java
new file mode 100644
index 0000000000..587c895b4d
--- /dev/null
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/config/CatalogConfigHandler.java
@@ -0,0 +1,104 @@
+/*
+ * 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.catalog.config;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.enterprise.inject.Any;
+import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.Instance.Handle;
+import jakarta.inject.Inject;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.iceberg.exceptions.NotFoundException;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.iceberg.rest.RESTCatalogProperties;
+import org.apache.iceberg.rest.responses.ConfigResponse;
+import org.apache.polaris.core.auth.PolarisPrincipal;
+import org.apache.polaris.core.entity.PolarisEntity;
+import org.apache.polaris.core.persistence.ResolvedPolarisEntity;
+import org.apache.polaris.core.persistence.resolver.Resolver;
+import org.apache.polaris.core.persistence.resolver.ResolverFactory;
+import org.apache.polaris.core.persistence.resolver.ResolverStatus;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
+import org.apache.polaris.core.rest.NamespaceUtils;
+import org.apache.polaris.service.catalog.CatalogPrefixParser;
+
+@RequestScoped
+public class CatalogConfigHandler {
+ private final CatalogPrefixParser prefixParser;
+ private final ResolverFactory resolverFactory;
+ private final Instance<CatalogConfigEndpointContributor> endpointContributors;
+
+ @Inject
+ public CatalogConfigHandler(
+ CatalogPrefixParser prefixParser,
+ ResolverFactory resolverFactory,
+ @Any Instance<CatalogConfigEndpointContributor> endpointContributors) {
+ this.prefixParser = prefixParser;
+ this.resolverFactory = resolverFactory;
+ this.endpointContributors = endpointContributors;
+ }
+
+ public ConfigResponse getConfig(String catalogName, PolarisPrincipal principal) {
+ Resolver resolver = resolverFactory.createResolver(principal, catalogName);
+ ResolverStatus resolverStatus = resolver.resolveAll();
+ if (!resolverStatus.getStatus().equals(ResolverStatus.StatusEnum.SUCCESS)) {
+ throw new NotFoundException("Unable to find warehouse %s", catalogName);
+ }
+ ResolvedPolarisEntity resolvedReferenceCatalog = resolver.getResolvedReferenceCatalog();
+ Map<String, String> properties =
+ PolarisEntity.of(resolvedReferenceCatalog.getEntity()).getPropertiesAsMap();
+
+ return ConfigResponse.builder()
+ .withDefaults(properties)
+ .withOverrides(
+ ImmutableMap.of(
+ "prefix",
+ prefixParser.catalogNameToPrefix(catalogName),
+ // Polaris does not handle custom namespace separators;
+ // always communicate the default namespace separator to clients.
+ RESTCatalogProperties.NAMESPACE_SEPARATOR,
+ NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR_ENCODED))
+ .withEndpoints(ImmutableList.copyOf(supportedEndpoints()))
+ .build();
+ }
+
+ private Set<Endpoint> supportedEndpoints() {
+ Set<Endpoint> endpoints = new LinkedHashSet<>();
+ endpointContributors
+ .handlesStream()
+ .sorted(
+ Comparator.comparingInt(CatalogConfigHandler::priority)
+ .thenComparing(handle -> handle.getBean().getBeanClass().getName()))
+ .map(Handle::get)
+ .map(CatalogConfigEndpointContributor::endpoints)
+ .forEach(endpoints::addAll);
+ return endpoints;
+ }
+
+ private static int priority(Handle<CatalogConfigEndpointContributor> handle) {
+ Priority priority = handle.getBean().getBeanClass().getAnnotation(Priority.class);
+ return priority == null ? Integer.MAX_VALUE : priority.value();
+ }
+}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableConfigEndpoints.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableConfigEndpoints.java
new file mode 100644
index 0000000000..2d68c25e3e
--- /dev/null
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableConfigEndpoints.java
@@ -0,0 +1,55 @@
+/*
+ * 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.catalog.generic;
+
+import com.google.common.collect.ImmutableSet;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.config.FeatureConfiguration;
+import org.apache.polaris.core.config.RealmConfig;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
+import org.apache.polaris.core.rest.GenericTableEndpoints;
+
+@ApplicationScoped
+@Priority(300)
+public class GenericTableConfigEndpoints implements CatalogConfigEndpointContributor {
+ private final RealmConfig realmConfig;
+
+ @Inject
+ public GenericTableConfigEndpoints(RealmConfig realmConfig) {
+ this.realmConfig = realmConfig;
+ }
+
+ @Override
+ public Set<Endpoint> endpoints() {
+ return getSupportedGenericTableEndpoints(realmConfig);
+ }
+
+ /**
+ * Get the generic table endpoints. Returns GENERIC_TABLE_ENDPOINTS if ENABLE_GENERIC_TABLES is
+ * set to true, otherwise, returns an empty set.
+ */
+ public static Set<Endpoint> getSupportedGenericTableEndpoints(RealmConfig realmConfig) {
+ boolean genericTableEnabled = realmConfig.getConfig(FeatureConfiguration.ENABLE_GENERIC_TABLES);
+ return genericTableEnabled ? GenericTableEndpoints.GENERIC_TABLE_ENDPOINTS : ImmutableSet.of();
+ }
+}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
index bff88f9639..427e3d093f 100644
--- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
@@ -57,6 +57,7 @@ import org.apache.polaris.service.catalog.CatalogPrefixParser;
import org.apache.polaris.service.catalog.api.IcebergRestCatalogApiService;
import org.apache.polaris.service.catalog.api.IcebergRestConfigurationApiService;
import org.apache.polaris.service.catalog.common.CatalogAdapter;
+import org.apache.polaris.service.catalog.config.CatalogConfigHandler;
import org.apache.polaris.service.catalog.validation.EntityNameValidator;
import org.apache.polaris.service.config.ReservedProperties;
import org.apache.polaris.service.http.IcebergHttpUtil;
@@ -81,17 +82,20 @@ public class IcebergCatalogAdapter
private final CatalogPrefixParser prefixParser;
private final ReservedProperties reservedProperties;
private final IcebergCatalogHandlerFactory handlerFactory;
+ private final CatalogConfigHandler configHandler;
@Inject
public IcebergCatalogAdapter(
CallContext callContext,
CatalogPrefixParser prefixParser,
ReservedProperties reservedProperties,
- IcebergCatalogHandlerFactory handlerFactory) {
+ IcebergCatalogHandlerFactory handlerFactory,
+ CatalogConfigHandler configHandler) {
this.realmConfig = callContext.getRealmConfig();
this.prefixParser = prefixParser;
this.reservedProperties = reservedProperties;
this.handlerFactory = handlerFactory;
+ this.configHandler = configHandler;
}
/**
@@ -764,7 +768,7 @@ public class IcebergCatalogAdapter
if (warehouse == null) {
throw new BadRequestException("Please specify a warehouse");
}
- return withCatalogByName(
- securityContext, warehouse, catalog -> Response.ok(catalog.getConfig()).build());
+ return Response.ok(configHandler.getConfig(warehouse, validatePrincipal(securityContext)))
+ .build();
}
}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java
index 89f68caa25..9eaef279e6 100644
--- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java
@@ -25,9 +25,6 @@ import static org.apache.polaris.service.catalog.common.ExceptionUtils.alreadyEx
import static org.apache.polaris.service.catalog.common.ExceptionUtils.notFoundExceptionForTableLikeEntity;
import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import io.smallrye.common.annotation.Identifier;
import jakarta.enterprise.inject.Instance;
@@ -67,11 +64,8 @@ import org.apache.iceberg.exceptions.BadRequestException;
import org.apache.iceberg.exceptions.CommitFailedException;
import org.apache.iceberg.exceptions.ForbiddenException;
import org.apache.iceberg.exceptions.NoSuchTableException;
-import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.io.FileIO;
import org.apache.iceberg.metrics.ScanReport;
-import org.apache.iceberg.rest.Endpoint;
-import org.apache.iceberg.rest.RESTCatalogProperties;
import org.apache.iceberg.rest.credentials.ImmutableCredential;
import org.apache.iceberg.rest.requests.CommitTransactionRequest;
import org.apache.iceberg.rest.requests.CreateNamespaceRequest;
@@ -83,7 +77,6 @@ import org.apache.iceberg.rest.requests.RenameTableRequest;
import org.apache.iceberg.rest.requests.ReportMetricsRequest;
import org.apache.iceberg.rest.requests.UpdateNamespacePropertiesRequest;
import org.apache.iceberg.rest.requests.UpdateTableRequest;
-import org.apache.iceberg.rest.responses.ConfigResponse;
import org.apache.iceberg.rest.responses.CreateNamespaceResponse;
import org.apache.iceberg.rest.responses.GetNamespaceResponse;
import org.apache.iceberg.rest.responses.ImmutableLoadCredentialsResponse;
@@ -110,18 +103,13 @@ import org.apache.polaris.core.entity.PolarisEntitySubType;
import org.apache.polaris.core.entity.PolarisEntityType;
import org.apache.polaris.core.entity.table.IcebergTableLikeEntity;
import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper;
-import org.apache.polaris.core.persistence.ResolvedPolarisEntity;
import org.apache.polaris.core.persistence.TransactionWorkspaceMetaStoreManager;
import org.apache.polaris.core.persistence.dao.entity.EntitiesResult;
import org.apache.polaris.core.persistence.dao.entity.EntityWithPath;
import org.apache.polaris.core.persistence.pagination.PageToken;
import org.apache.polaris.core.persistence.resolver.ResolvedPathKey;
-import org.apache.polaris.core.persistence.resolver.Resolver;
import org.apache.polaris.core.persistence.resolver.ResolverFactory;
import org.apache.polaris.core.persistence.resolver.ResolverPath;
-import org.apache.polaris.core.persistence.resolver.ResolverStatus;
-import org.apache.polaris.core.rest.NamespaceUtils;
-import org.apache.polaris.core.rest.PolarisEndpoints;
import org.apache.polaris.core.storage.PolarisStorageActions;
import org.apache.polaris.core.storage.StorageAccessConfig;
import org.apache.polaris.core.storage.StorageUtil;
@@ -166,38 +154,6 @@ import org.slf4j.LoggerFactory;
public abstract class IcebergCatalogHandler extends CatalogHandler implements AutoCloseable {
private static final Logger LOGGER = LoggerFactory.getLogger(IcebergCatalogHandler.class);
- private static final Set<Endpoint> DEFAULT_ENDPOINTS =
- ImmutableSet.<Endpoint>builder()
- .add(Endpoint.V1_LIST_NAMESPACES)
- .add(Endpoint.V1_LOAD_NAMESPACE)
- .add(Endpoint.V1_NAMESPACE_EXISTS)
- .add(Endpoint.V1_CREATE_NAMESPACE)
- .add(Endpoint.V1_UPDATE_NAMESPACE)
- .add(Endpoint.V1_DELETE_NAMESPACE)
- .add(Endpoint.V1_LIST_TABLES)
- .add(Endpoint.V1_LOAD_TABLE)
- .add(Endpoint.V1_TABLE_EXISTS)
- .add(Endpoint.V1_CREATE_TABLE)
- .add(Endpoint.V1_UPDATE_TABLE)
- .add(Endpoint.V1_DELETE_TABLE)
- .add(Endpoint.V1_RENAME_TABLE)
- .add(Endpoint.V1_REGISTER_TABLE)
- .add(Endpoint.V1_REPORT_METRICS)
- .add(Endpoint.V1_COMMIT_TRANSACTION)
- .build();
-
- private static final Set<Endpoint> VIEW_ENDPOINTS =
- ImmutableSet.<Endpoint>builder()
- .add(Endpoint.V1_LIST_VIEWS)
- .add(Endpoint.V1_LOAD_VIEW)
- .add(Endpoint.V1_VIEW_EXISTS)
- .add(Endpoint.V1_CREATE_VIEW)
- .add(Endpoint.V1_UPDATE_VIEW)
- .add(Endpoint.V1_DELETE_VIEW)
- .add(Endpoint.V1_RENAME_VIEW)
- .add(Endpoint.V1_REGISTER_VIEW)
- .build();
-
protected abstract PolarisDiagnostics diagnostics();
protected abstract PolarisCredentialManager credentialManager();
@@ -1619,37 +1575,6 @@ public abstract class IcebergCatalogHandler extends CatalogHandler implements Au
}
}
- public ConfigResponse getConfig() {
- Resolver resolver = resolverFactory().createResolver(polarisPrincipal(), catalogName());
- ResolverStatus resolverStatus = resolver.resolveAll();
- if (!resolverStatus.getStatus().equals(ResolverStatus.StatusEnum.SUCCESS)) {
- throw new NotFoundException("Unable to find warehouse %s", catalogName());
- }
- ResolvedPolarisEntity resolvedReferenceCatalog = resolver.getResolvedReferenceCatalog();
- Map<String, String> properties =
- PolarisEntity.of(resolvedReferenceCatalog.getEntity()).getPropertiesAsMap();
-
- return ConfigResponse.builder()
- .withDefaults(properties) // catalog properties are defaults
- .withOverrides(
- ImmutableMap.of(
- "prefix",
- prefixParser().catalogNameToPrefix(catalogName()),
- // Polaris does not handle custom namespace separators;
- // always communicate the default namespace separator to clients.
- RESTCatalogProperties.NAMESPACE_SEPARATOR,
- NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR_ENCODED))
- .withEndpoints(
- ImmutableList.<Endpoint>builder()
- .addAll(DEFAULT_ENDPOINTS)
- .addAll(VIEW_ENDPOINTS)
- .addAll(PolarisEndpoints.getSupportedGenericTableEndpoints(realmConfig()))
- .addAll(PolarisEndpoints.getSupportedPolicyEndpoints(realmConfig()))
- .addAll(PolarisEndpoints.getSupportedSemanticModelEndpoints(realmConfig()))
- .build())
- .build();
- }
-
private StorageAccessConfig vendCredentials(
TableIdentifier tableIdentifier,
Set<String> tableLocations,
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergRestConfigEndpoints.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergRestConfigEndpoints.java
new file mode 100644
index 0000000000..f2da605c3a
--- /dev/null
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergRestConfigEndpoints.java
@@ -0,0 +1,55 @@
+/*
+ * 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.catalog.iceberg;
+
+import com.google.common.collect.ImmutableSet;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
+
+@ApplicationScoped
+@Priority(100)
+public class IcebergRestConfigEndpoints implements CatalogConfigEndpointContributor {
+ public static final Set<Endpoint> ICEBERG_REST_ENDPOINTS =
+ ImmutableSet.<Endpoint>builder()
+ .add(Endpoint.V1_LIST_NAMESPACES)
+ .add(Endpoint.V1_LOAD_NAMESPACE)
+ .add(Endpoint.V1_NAMESPACE_EXISTS)
+ .add(Endpoint.V1_CREATE_NAMESPACE)
+ .add(Endpoint.V1_UPDATE_NAMESPACE)
+ .add(Endpoint.V1_DELETE_NAMESPACE)
+ .add(Endpoint.V1_LIST_TABLES)
+ .add(Endpoint.V1_LOAD_TABLE)
+ .add(Endpoint.V1_TABLE_EXISTS)
+ .add(Endpoint.V1_CREATE_TABLE)
+ .add(Endpoint.V1_UPDATE_TABLE)
+ .add(Endpoint.V1_DELETE_TABLE)
+ .add(Endpoint.V1_RENAME_TABLE)
+ .add(Endpoint.V1_REGISTER_TABLE)
+ .add(Endpoint.V1_REPORT_METRICS)
+ .add(Endpoint.V1_COMMIT_TRANSACTION)
+ .build();
+
+ @Override
+ public Set<Endpoint> endpoints() {
+ return ICEBERG_REST_ENDPOINTS;
+ }
+}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergViewConfigEndpoints.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergViewConfigEndpoints.java
new file mode 100644
index 0000000000..aecf88d730
--- /dev/null
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergViewConfigEndpoints.java
@@ -0,0 +1,47 @@
+/*
+ * 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.catalog.iceberg;
+
+import com.google.common.collect.ImmutableSet;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
+
+@ApplicationScoped
+@Priority(200)
+public class IcebergViewConfigEndpoints implements CatalogConfigEndpointContributor {
+ public static final Set<Endpoint> ICEBERG_VIEW_ENDPOINTS =
+ ImmutableSet.<Endpoint>builder()
+ .add(Endpoint.V1_LIST_VIEWS)
+ .add(Endpoint.V1_LOAD_VIEW)
+ .add(Endpoint.V1_VIEW_EXISTS)
+ .add(Endpoint.V1_CREATE_VIEW)
+ .add(Endpoint.V1_UPDATE_VIEW)
+ .add(Endpoint.V1_DELETE_VIEW)
+ .add(Endpoint.V1_RENAME_VIEW)
+ .add(Endpoint.V1_REGISTER_VIEW)
+ .build();
+
+ @Override
+ public Set<Endpoint> endpoints() {
+ return ICEBERG_VIEW_ENDPOINTS;
+ }
+}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyConfigEndpoints.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyConfigEndpoints.java
new file mode 100644
index 0000000000..f17927a40d
--- /dev/null
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyConfigEndpoints.java
@@ -0,0 +1,54 @@
+/*
+ * 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.catalog.policy;
+
+import com.google.common.collect.ImmutableSet;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.config.FeatureConfiguration;
+import org.apache.polaris.core.config.RealmConfig;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
+
+@ApplicationScoped
+@Priority(400)
+public class PolicyConfigEndpoints implements CatalogConfigEndpointContributor {
+ private final RealmConfig realmConfig;
+
+ @Inject
+ public PolicyConfigEndpoints(RealmConfig realmConfig) {
+ this.realmConfig = realmConfig;
+ }
+
+ @Override
+ public Set<Endpoint> endpoints() {
+ return getSupportedPolicyEndpoints(realmConfig);
+ }
+
+ /**
+ * Get the policy store endpoints. Returns POLICY_ENDPOINTS if ENABLE_POLICY_STORE is set to true,
+ * otherwise, returns an empty set.
+ */
+ public static Set<Endpoint> getSupportedPolicyEndpoints(RealmConfig realmConfig) {
+ boolean policyStoreEnabled = realmConfig.getConfig(FeatureConfiguration.ENABLE_POLICY_STORE);
+ return policyStoreEnabled ? PolicyEndpoints.POLICY_STORE_ENDPOINTS : ImmutableSet.of();
+ }
+}
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyEndpoints.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyEndpoints.java
new file mode 100644
index 0000000000..15d413afe3
--- /dev/null
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyEndpoints.java
@@ -0,0 +1,57 @@
+/*
+ * 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.catalog.policy;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.apache.iceberg.rest.Endpoint;
+import org.apache.polaris.core.rest.PolarisResourcePaths;
+
+public class PolicyEndpoints {
+ private PolicyEndpoints() {}
+
+ public static final Endpoint V1_LIST_POLICIES =
+ Endpoint.create("GET", PolarisResourcePaths.V1_POLICIES);
+ public static final Endpoint V1_CREATE_POLICY =
+ Endpoint.create("POST", PolarisResourcePaths.V1_POLICIES);
+ public static final Endpoint V1_LOAD_POLICY =
+ Endpoint.create("GET", PolarisResourcePaths.V1_POLICY);
+ public static final Endpoint V1_UPDATE_POLICY =
+ Endpoint.create("PUT", PolarisResourcePaths.V1_POLICY);
+ public static final Endpoint V1_DROP_POLICY =
+ Endpoint.create("DELETE", PolarisResourcePaths.V1_POLICY);
+ public static final Endpoint V1_ATTACH_POLICY =
+ Endpoint.create("PUT", PolarisResourcePaths.V1_POLICY_MAPPINGS);
+ public static final Endpoint V1_DETACH_POLICY =
+ Endpoint.create("POST", PolarisResourcePaths.V1_POLICY_MAPPINGS);
+ public static final Endpoint V1_GET_APPLICABLE_POLICIES =
+ Endpoint.create("GET", PolarisResourcePaths.V1_APPLICABLE_POLICIES);
+
+ public static final Set<Endpoint> POLICY_STORE_ENDPOINTS =
+ ImmutableSet.<Endpoint>builder()
+ .add(V1_LIST_POLICIES)
+ .add(V1_CREATE_POLICY)
+ .add(V1_LOAD_POLICY)
+ .add(V1_UPDATE_POLICY)
+ .add(V1_DROP_POLICY)
+ .add(V1_ATTACH_POLICY)
+ .add(V1_DETACH_POLICY)
+ .add(V1_GET_APPLICABLE_POLICIES)
+ .build();
+}
diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/GetConfigTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/GetConfigTest.java
index 8885fcd467..a7a0cb6fe1 100644
--- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/GetConfigTest.java
+++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/GetConfigTest.java
@@ -25,6 +25,7 @@ import jakarta.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import org.apache.iceberg.rest.Endpoint;
import org.apache.iceberg.rest.responses.ConfigResponse;
import org.apache.polaris.core.admin.model.Catalog;
import org.apache.polaris.core.admin.model.CatalogProperties;
@@ -32,8 +33,9 @@ import org.apache.polaris.core.admin.model.CreateCatalogRequest;
import org.apache.polaris.core.admin.model.FileStorageConfigInfo;
import org.apache.polaris.core.admin.model.PolarisCatalog;
import org.apache.polaris.core.admin.model.StorageConfigInfo;
-import org.apache.polaris.core.rest.PolarisEndpoints;
+import org.apache.polaris.core.rest.GenericTableEndpoints;
import org.apache.polaris.service.TestServices;
+import org.apache.polaris.service.catalog.policy.PolicyEndpoints;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -80,20 +82,49 @@ public class GetConfigTest {
.restConfigurationApi()
.getConfig(catalogName, services.realmContext(), services.securityContext());
ConfigResponse configResponse = response.readEntity(ConfigResponse.class);
+
assertThat(configResponse.overrides()).contains(Map.entry("prefix", catalogName));
+ assertThat(configResponse.endpoints()).contains(PolicyEndpoints.V1_CREATE_POLICY);
+ assertEndpointOrder(configResponse, enableGenericTable);
+ assertGenericTableEndpoints(configResponse, enableGenericTable);
+ }
+
+ private static void assertEndpointOrder(
+ ConfigResponse configResponse, boolean enableGenericTable) {
if (enableGenericTable) {
- assertThat(configResponse.endpoints()).contains(PolarisEndpoints.V1_CREATE_GENERIC_TABLE);
- assertThat(configResponse.endpoints()).contains(PolarisEndpoints.V1_DELETE_GENERIC_TABLE);
- assertThat(configResponse.endpoints()).contains(PolarisEndpoints.V1_LIST_GENERIC_TABLES);
- assertThat(configResponse.endpoints()).contains(PolarisEndpoints.V1_LOAD_GENERIC_TABLE);
+ assertThat(configResponse.endpoints())
+ .containsSubsequence(
+ Endpoint.V1_LIST_NAMESPACES,
+ Endpoint.V1_REGISTER_VIEW,
+ GenericTableEndpoints.V1_CREATE_GENERIC_TABLE,
+ PolicyEndpoints.V1_CREATE_POLICY);
} else {
assertThat(configResponse.endpoints())
- .doesNotContain(PolarisEndpoints.V1_CREATE_GENERIC_TABLE);
+ .containsSubsequence(
+ Endpoint.V1_LIST_NAMESPACES,
+ Endpoint.V1_REGISTER_VIEW,
+ PolicyEndpoints.V1_CREATE_POLICY);
+ }
+ }
+
+ private static void assertGenericTableEndpoints(
+ ConfigResponse configResponse, boolean enableGenericTable) {
+ if (enableGenericTable) {
+ assertThat(configResponse.endpoints())
+ .contains(GenericTableEndpoints.V1_CREATE_GENERIC_TABLE);
+ assertThat(configResponse.endpoints())
+ .contains(GenericTableEndpoints.V1_DELETE_GENERIC_TABLE);
+ assertThat(configResponse.endpoints()).contains(GenericTableEndpoints.V1_LIST_GENERIC_TABLES);
+ assertThat(configResponse.endpoints()).contains(GenericTableEndpoints.V1_LOAD_GENERIC_TABLE);
+ } else {
+ assertThat(configResponse.endpoints())
+ .doesNotContain(GenericTableEndpoints.V1_CREATE_GENERIC_TABLE);
+ assertThat(configResponse.endpoints())
+ .doesNotContain(GenericTableEndpoints.V1_DELETE_GENERIC_TABLE);
assertThat(configResponse.endpoints())
- .doesNotContain(PolarisEndpoints.V1_DELETE_GENERIC_TABLE);
+ .doesNotContain(GenericTableEndpoints.V1_LIST_GENERIC_TABLES);
assertThat(configResponse.endpoints())
- .doesNotContain(PolarisEndpoints.V1_LIST_GENERIC_TABLES);
- assertThat(configResponse.endpoints()).doesNotContain(PolarisEndpoints.V1_LOAD_GENERIC_TABLE);
+ .doesNotContain(GenericTableEndpoints.V1_LOAD_GENERIC_TABLE);
}
}
}
diff --git a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java
index e0b9d5ae86..6fc92e0078 100644
--- a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java
+++ b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java
@@ -24,6 +24,8 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.Instance.Handle;
+import jakarta.enterprise.inject.spi.Bean;
import jakarta.ws.rs.core.SecurityContext;
import java.security.Principal;
import java.time.Clock;
@@ -34,6 +36,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
import org.apache.polaris.core.PolarisCallContext;
import org.apache.polaris.core.PolarisDefaultDiagServiceImpl;
import org.apache.polaris.core.PolarisDiagnostics;
@@ -60,6 +63,7 @@ import org.apache.polaris.core.persistence.resolver.ResolutionManifestFactory;
import org.apache.polaris.core.persistence.resolver.ResolutionManifestFactoryImpl;
import org.apache.polaris.core.persistence.resolver.Resolver;
import org.apache.polaris.core.persistence.resolver.ResolverFactory;
+import org.apache.polaris.core.rest.CatalogConfigEndpointContributor;
import org.apache.polaris.core.secrets.UserSecretsManager;
import org.apache.polaris.core.secrets.UserSecretsManagerFactory;
import org.apache.polaris.core.storage.cache.StorageCredentialCache;
@@ -75,21 +79,26 @@ import org.apache.polaris.service.catalog.api.IcebergRestConfigurationApi;
import org.apache.polaris.service.catalog.api.IcebergRestConfigurationApiService;
import org.apache.polaris.service.catalog.api.PolarisCatalogGenericTableApi;
import org.apache.polaris.service.catalog.api.PolarisCatalogGenericTableApiService;
+import org.apache.polaris.service.catalog.config.CatalogConfigHandler;
import org.apache.polaris.service.catalog.generic.CatalogGenericTableEventServiceDelegator;
import org.apache.polaris.service.catalog.generic.GenericTableCatalogAdapter;
import org.apache.polaris.service.catalog.generic.GenericTableCatalogHandler;
import org.apache.polaris.service.catalog.generic.GenericTableCatalogHandlerFactory;
+import org.apache.polaris.service.catalog.generic.GenericTableConfigEndpoints;
import org.apache.polaris.service.catalog.generic.ImmutableGenericTableCatalogHandler;
import org.apache.polaris.service.catalog.iceberg.CatalogHandlerUtils;
import org.apache.polaris.service.catalog.iceberg.IcebergCatalogAdapter;
import org.apache.polaris.service.catalog.iceberg.IcebergCatalogHandler;
import org.apache.polaris.service.catalog.iceberg.IcebergCatalogHandlerFactory;
import org.apache.polaris.service.catalog.iceberg.IcebergRestCatalogEventServiceDelegator;
+import org.apache.polaris.service.catalog.iceberg.IcebergRestConfigEndpoints;
import org.apache.polaris.service.catalog.iceberg.IcebergRestConfigurationEventServiceDelegator;
+import org.apache.polaris.service.catalog.iceberg.IcebergViewConfigEndpoints;
import org.apache.polaris.service.catalog.iceberg.ImmutableIcebergCatalogHandler;
import org.apache.polaris.service.catalog.io.FileIOFactory;
import org.apache.polaris.service.catalog.io.MeasuredFileIOFactory;
import org.apache.polaris.service.catalog.io.StorageAccessConfigProvider;
+import org.apache.polaris.service.catalog.policy.PolicyConfigEndpoints;
import org.apache.polaris.service.config.ReservedProperties;
import org.apache.polaris.service.context.catalog.PolarisLocalCatalogFactory;
import org.apache.polaris.service.credentials.DefaultPolarisCredentialManager;
@@ -374,6 +383,34 @@ public record TestServices(
EventAttributeMap eventAttributeMap = new EventAttributeMap();
+ Supplier<CatalogConfigHandler> catalogConfigHandlerSupplier =
+ () -> {
+ @SuppressWarnings("unchecked")
+ Instance<CatalogConfigEndpointContributor> configEndpointContributors =
+ Mockito.mock(Instance.class);
+ CatalogConfigEndpointContributor icebergRestEndpoints =
+ new IcebergRestConfigEndpoints();
+ CatalogConfigEndpointContributor icebergViewEndpoints =
+ new IcebergViewConfigEndpoints();
+ CatalogConfigEndpointContributor genericTableEndpoints =
+ new GenericTableConfigEndpoints(realmConfig);
+ CatalogConfigEndpointContributor policyEndpoints =
+ new PolicyConfigEndpoints(realmConfig);
+ Mockito.when(configEndpointContributors.handlesStream())
+ .thenAnswer(
+ invocation ->
+ Stream.of(
+ endpointContributorHandle(
+ GenericTableConfigEndpoints.class, genericTableEndpoints),
+ endpointContributorHandle(PolicyConfigEndpoints.class, policyEndpoints),
+ endpointContributorHandle(
+ IcebergRestConfigEndpoints.class, icebergRestEndpoints),
+ endpointContributorHandle(
+ IcebergViewConfigEndpoints.class, icebergViewEndpoints)));
+ return new CatalogConfigHandler(
+ new DefaultCatalogPrefixParser(), resolverFactory, configEndpointContributors);
+ };
+
Supplier<IcebergCatalogAdapter> catalogAdapterSupplier =
() -> {
IcebergCatalogHandlerFactory handlerFactory =
@@ -407,7 +444,11 @@ public record TestServices(
};
return new IcebergCatalogAdapter(
- callContext, new DefaultCatalogPrefixParser(), reservedProperties, handlerFactory);
+ callContext,
+ new DefaultCatalogPrefixParser(),
+ reservedProperties,
+ handlerFactory,
+ catalogConfigHandlerSupplier.get());
};
Supplier<IcebergRestCatalogApi> restApiSupplier =
@@ -518,6 +559,18 @@ public record TestServices(
}
}
+ @SuppressWarnings("unchecked")
+ private static Handle<CatalogConfigEndpointContributor> endpointContributorHandle(
+ Class<? extends CatalogConfigEndpointContributor> beanClass,
+ CatalogConfigEndpointContributor contributor) {
+ Handle<CatalogConfigEndpointContributor> handle = Mockito.mock(Handle.class);
+ Bean<CatalogConfigEndpointContributor> bean = Mockito.mock(Bean.class);
+ Mockito.doReturn(beanClass).when(bean).getBeanClass();
+ Mockito.when(handle.getBean()).thenReturn(bean);
+ Mockito.when(handle.get()).thenReturn(contributor);
+ return handle;
+ }
+
public PolarisCallContext newCallContext() {
return new PolarisCallContext(
realmContext,