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 dd3c71c64 Core: Add Endpoints and resource paths for Generic Table
(#1286)
dd3c71c64 is described below
commit dd3c71c64f0f1eadfddb125f173601468a36996d
Author: gh-yzou <[email protected]>
AuthorDate: Wed Apr 2 23:15:28 2025 -0700
Core: Add Endpoints and resource paths for Generic Table (#1286)
---
.../apache/polaris/core/rest/PolarisEndpoints.java | 60 +++++++++++++++
.../polaris/core/rest/PolarisResourcePaths.java | 62 +++++++++++++++
.../core/rest/PolarisResourcePathsTest.java | 60 +++++++++++++++
.../service/quarkus/catalog/GetConfigTest.java | 87 ++++++++++++++++++++++
.../catalog/iceberg/IcebergCatalogAdapter.java | 2 +
.../org/apache/polaris/service/TestServices.java | 7 +-
6 files changed, 276 insertions(+), 2 deletions(-)
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
new file mode 100644
index 000000000..edbfb1414
--- /dev/null
+++
b/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisEndpoints.java
@@ -0,0 +1,60 @@
+/*
+ * 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.context.CallContext;
+
+public class PolarisEndpoints {
+ 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();
+
+ /**
+ * 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(CallContext
callContext) {
+ // add the generic table endpoints as supported endpoints if generic table
feature is enabled.
+ boolean genericTableEnabled =
+ callContext
+ .getPolarisCallContext()
+ .getConfigurationStore()
+ .getConfiguration(
+ callContext.getPolarisCallContext(),
FeatureConfiguration.ENABLE_GENERIC_TABLES);
+
+ return genericTableEnabled ? GENERIC_TABLE_ENDPOINTS : ImmutableSet.of();
+ }
+}
diff --git
a/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java
b/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java
new file mode 100644
index 000000000..159a1a014
--- /dev/null
+++
b/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java
@@ -0,0 +1,62 @@
+/*
+ * 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.base.Joiner;
+import java.util.Map;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.rest.RESTUtil;
+
+public class PolarisResourcePaths {
+ private static final Joiner SLASH = Joiner.on("/").skipNulls();
+ public static final String PREFIX = "prefix";
+
+ // Generic Table endpoints
+ public static final String V1_GENERIC_TABLES =
+ "polaris/v1/{prefix}/namespaces/{namespace}/generic-tables";
+ public static final String V1_GENERIC_TABLE =
+
"polaris/v1/{prefix}/namespaces/{namespace}/generic-tables/{generic-table}";
+
+ private final String prefix;
+
+ public PolarisResourcePaths(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public static PolarisResourcePaths forCatalogProperties(Map<String, String>
properties) {
+ return new PolarisResourcePaths(properties.get(PREFIX));
+ }
+
+ public String genericTables(Namespace ns) {
+ return SLASH.join(
+ "polaris", "v1", prefix, "namespaces", RESTUtil.encodeNamespace(ns),
"generic-tables");
+ }
+
+ public String genericTable(TableIdentifier ident) {
+ return SLASH.join(
+ "polaris",
+ "v1",
+ prefix,
+ "namespaces",
+ RESTUtil.encodeNamespace(ident.namespace()),
+ "generic-tables",
+ RESTUtil.encodeString(ident.name()));
+ }
+}
diff --git
a/polaris-core/src/test/java/org/apache/polaris/core/rest/PolarisResourcePathsTest.java
b/polaris-core/src/test/java/org/apache/polaris/core/rest/PolarisResourcePathsTest.java
new file mode 100644
index 000000000..4572bb515
--- /dev/null
+++
b/polaris-core/src/test/java/org/apache/polaris/core/rest/PolarisResourcePathsTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class PolarisResourcePathsTest {
+ private static final String testPrefix = "polaris-test";
+
+ private PolarisResourcePaths paths;
+
+ @BeforeEach
+ public void setUp() {
+ Map<String, String> properties = new HashMap<>();
+ properties.put(PolarisResourcePaths.PREFIX, testPrefix);
+ paths = PolarisResourcePaths.forCatalogProperties(properties);
+ }
+
+ @Test
+ public void testGenericTablesPath() {
+ Namespace ns = Namespace.of("ns1", "ns2");
+ String genericTablesPath = paths.genericTables(ns);
+ String expectedPath =
+ String.format("polaris/v1/%s/namespaces/%s/generic-tables",
testPrefix, "ns1%1Fns2");
+ Assertions.assertThat(genericTablesPath).isEqualTo(expectedPath);
+ }
+
+ @Test
+ public void testGenericTablePath() {
+ Namespace ns = Namespace.of("ns1");
+ TableIdentifier ident = TableIdentifier.of(ns, "test-table");
+ String genericTablePath = paths.genericTable(ident);
+ String expectedPath =
+ String.format(
+ "polaris/v1/%s/namespaces/%s/generic-tables/%s", testPrefix,
"ns1", "test-table");
+ Assertions.assertThat(genericTablePath).isEqualTo(expectedPath);
+ }
+}
diff --git
a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GetConfigTest.java
b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GetConfigTest.java
new file mode 100644
index 000000000..5e77d6aea
--- /dev/null
+++
b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/GetConfigTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.quarkus.catalog;
+
+import static jakarta.ws.rs.core.Response.Status.CREATED;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.quarkus.test.junit.QuarkusTest;
+import jakarta.ws.rs.core.Response;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.iceberg.rest.responses.ConfigResponse;
+import org.apache.polaris.core.admin.model.*;
+import org.apache.polaris.core.rest.PolarisEndpoints;
+import org.apache.polaris.service.TestServices;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+@QuarkusTest
+public class GetConfigTest {
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testGetConfig(boolean enableGenericTable) {
+ TestServices services =
+ TestServices.builder().config(Map.of("ENABLE_GENERIC_TABLES",
enableGenericTable)).build();
+
+ FileStorageConfigInfo fileStorage =
+ FileStorageConfigInfo.builder(StorageConfigInfo.StorageTypeEnum.FILE)
+ .setAllowedLocations(List.of("file://"))
+ .build();
+ String catalogName = "test-catalog-" + UUID.randomUUID();
+ Catalog catalog =
+ PolarisCatalog.builder()
+ .setType(Catalog.TypeEnum.INTERNAL)
+ .setName(catalogName)
+ .setProperties(new CatalogProperties("file:///tmp/path/to/data"))
+ .setStorageConfigInfo(fileStorage)
+ .build();
+
+ Response response =
+ services
+ .catalogsApi()
+ .createCatalog(
+ new CreateCatalogRequest(catalog),
+ services.realmContext(),
+ services.securityContext());
+ assertThat(response.getStatus()).isEqualTo(CREATED.getStatusCode());
+
+ response =
+ services
+ .restConfigurationApi()
+ .getConfig(catalogName, services.realmContext(),
services.securityContext());
+ ConfigResponse configResponse = response.readEntity(ConfigResponse.class);
+ assertThat(configResponse.overrides()).contains(Map.entry("prefix",
catalogName));
+ 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);
+ } else {
+ assertThat(configResponse.endpoints())
+ .doesNotContain(PolarisEndpoints.V1_CREATE_GENERIC_TABLE);
+ assertThat(configResponse.endpoints())
+ .doesNotContain(PolarisEndpoints.V1_DELETE_GENERIC_TABLE);
+ assertThat(configResponse.endpoints())
+ .doesNotContain(PolarisEndpoints.V1_LIST_GENERIC_TABLES);
+
assertThat(configResponse.endpoints()).doesNotContain(PolarisEndpoints.V1_LOAD_GENERIC_TABLE);
+ }
+ }
+}
diff --git
a/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
b/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
index ccb6b44f5..9bab20586 100644
---
a/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
+++
b/service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
@@ -67,6 +67,7 @@ import
org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.persistence.ResolvedPolarisEntity;
import org.apache.polaris.core.persistence.resolver.Resolver;
import org.apache.polaris.core.persistence.resolver.ResolverStatus;
+import org.apache.polaris.core.rest.PolarisEndpoints;
import org.apache.polaris.service.catalog.AccessDelegationMode;
import org.apache.polaris.service.catalog.CatalogPrefixParser;
import org.apache.polaris.service.catalog.api.IcebergRestCatalogApiService;
@@ -723,6 +724,7 @@ public class IcebergCatalogAdapter
.addAll(DEFAULT_ENDPOINTS)
.addAll(VIEW_ENDPOINTS)
.addAll(COMMIT_ENDPOINT)
+
.addAll(PolarisEndpoints.getSupportedGenericTableEndpoints(callContext))
.build())
.build())
.build();
diff --git
a/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java
b/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java
index c763af661..d55021792 100644
---
a/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java
+++
b/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java
@@ -46,7 +46,7 @@ import org.apache.polaris.service.admin.PolarisServiceImpl;
import org.apache.polaris.service.admin.api.PolarisCatalogsApi;
import org.apache.polaris.service.catalog.DefaultCatalogPrefixParser;
import org.apache.polaris.service.catalog.api.IcebergRestCatalogApi;
-import org.apache.polaris.service.catalog.api.IcebergRestCatalogApiService;
+import org.apache.polaris.service.catalog.api.IcebergRestConfigurationApi;
import org.apache.polaris.service.catalog.iceberg.IcebergCatalogAdapter;
import org.apache.polaris.service.catalog.io.FileIOFactory;
import org.apache.polaris.service.catalog.io.MeasuredFileIOFactory;
@@ -64,6 +64,7 @@ import software.amazon.awssdk.services.sts.StsClient;
public record TestServices(
PolarisCatalogsApi catalogsApi,
IcebergRestCatalogApi restApi,
+ IcebergRestConfigurationApi restConfigurationApi,
PolarisConfigurationStore configurationStore,
PolarisDiagnostics polarisDiagnostics,
RealmEntityManagerFactory entityManagerFactory,
@@ -170,7 +171,7 @@ public record TestServices(
new PolarisCallContextCatalogFactory(
realmEntityManagerFactory, metaStoreManagerFactory,
taskExecutor, fileIOFactory);
- IcebergRestCatalogApiService service =
+ IcebergCatalogAdapter service =
new IcebergCatalogAdapter(
realmContext,
callContext,
@@ -181,6 +182,7 @@ public record TestServices(
new DefaultCatalogPrefixParser());
IcebergRestCatalogApi restApi = new IcebergRestCatalogApi(service);
+ IcebergRestConfigurationApi restConfigurationApi = new
IcebergRestConfigurationApi(service);
CreatePrincipalResult createdPrincipal =
metaStoreManager.createPrincipal(
@@ -225,6 +227,7 @@ public record TestServices(
return new TestServices(
catalogsApi,
restApi,
+ restConfigurationApi,
configurationStore,
polarisDiagnostics,
realmEntityManagerFactory,