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 b1f7c68b Policy Store: Add PolicyEntity and PolicyTypes (#1133)
b1f7c68b is described below

commit b1f7c68b1cb29dfdce6b401c0c12b5bf0305a6a5
Author: Honah (Jonas) J. <[email protected]>
AuthorDate: Tue Mar 18 11:38:34 2025 -0700

    Policy Store: Add PolicyEntity and PolicyTypes (#1133)
---
 .../polaris/core/entity/PolarisEntityType.java     |   3 +-
 .../apache/polaris/core/policy/PolicyEntity.java   | 129 +++++++++++++++++++++
 .../org/apache/polaris/core/policy/PolicyType.java |  84 ++++++++++++++
 .../polaris/core/policy/PredefinedPolicyTypes.java | 108 +++++++++++++++++
 .../polaris/core/persistence/ResolverTest.java     |  16 +++
 .../polaris/core/policy/PolicyEntityTest.java      |  65 +++++++++++
 .../apache/polaris/core/policy/PolicyTypeTest.java |  56 +++++++++
 .../persistence/PolarisTestMetaStoreManager.java   | 105 ++++++++++++++++-
 8 files changed, 562 insertions(+), 4 deletions(-)

diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityType.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityType.java
index af50eed6..4a3eada3 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityType.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityType.java
@@ -34,7 +34,8 @@ public enum PolarisEntityType {
   // generic table is either a view or a real table
   TABLE_LIKE(7, NAMESPACE, false, false),
   TASK(8, ROOT, false, false),
-  FILE(9, TABLE_LIKE, false, false);
+  FILE(9, TABLE_LIKE, false, false),
+  POLICY(10, NAMESPACE, false, false);
 
   // to efficiently map a code to its corresponding entity type, use a reverse 
array which
   // is initialized below
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyEntity.java 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyEntity.java
new file mode 100644
index 00000000..470822bb
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyEntity.java
@@ -0,0 +1,129 @@
+/*
+ * 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.policy;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.common.base.Preconditions;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.rest.RESTUtil;
+import org.apache.polaris.core.entity.NamespaceEntity;
+import org.apache.polaris.core.entity.PolarisBaseEntity;
+import org.apache.polaris.core.entity.PolarisEntity;
+import org.apache.polaris.core.entity.PolarisEntityType;
+
+public class PolicyEntity extends PolarisEntity {
+
+  public static final String POLICY_TYPE_CODE_KEY = "policy-type-code";
+  public static final String POLICY_DESCRIPTION_KEY = "policy-description";
+  public static final String POLICY_VERSION_KEY = "policy-version";
+  public static final String POLICY_CONTENT_KEY = "policy-content";
+
+  PolicyEntity(PolarisBaseEntity sourceEntity) {
+    super(sourceEntity);
+  }
+
+  public static PolicyEntity of(PolarisBaseEntity sourceEntity) {
+    if (sourceEntity != null) {
+      return new PolicyEntity(sourceEntity);
+    }
+
+    return null;
+  }
+
+  @JsonIgnore
+  public PolicyType getPolicyType() {
+    return PolicyType.fromCode(getPolicyTypeCode());
+  }
+
+  @JsonIgnore
+  public int getPolicyTypeCode() {
+    Preconditions.checkArgument(
+        getPropertiesAsMap().containsKey(POLICY_TYPE_CODE_KEY),
+        "Invalid policy entity: policy type must exist");
+    String policyTypeCode = getPropertiesAsMap().get(POLICY_TYPE_CODE_KEY);
+    return Integer.parseInt(policyTypeCode);
+  }
+
+  @JsonIgnore
+  public String getDescription() {
+    return getPropertiesAsMap().get(POLICY_DESCRIPTION_KEY);
+  }
+
+  @JsonIgnore
+  public String getContent() {
+    return getPropertiesAsMap().get(POLICY_CONTENT_KEY);
+  }
+
+  @JsonIgnore
+  public int getPolicyVersion() {
+    return Integer.parseInt(getPropertiesAsMap().get(POLICY_VERSION_KEY));
+  }
+
+  public static class Builder extends PolarisEntity.BaseBuilder<PolicyEntity, 
Builder> {
+    public Builder(Namespace namespace, String policyName, PolicyType 
policyType) {
+      super();
+      setType(PolarisEntityType.POLICY);
+      setParentNamespace(namespace);
+      setName(policyName);
+      setPolicyType(policyType);
+      setPolicyVersion(0);
+    }
+
+    public Builder(PolicyEntity original) {
+      super(original);
+    }
+
+    @Override
+    public PolicyEntity build() {
+      Preconditions.checkArgument(
+          properties.containsKey(POLICY_TYPE_CODE_KEY), "Policy type must be 
specified");
+
+      return new PolicyEntity(buildBase());
+    }
+
+    public Builder setParentNamespace(Namespace namespace) {
+      if (namespace != null && !namespace.isEmpty()) {
+        internalProperties.put(
+            NamespaceEntity.PARENT_NAMESPACE_KEY, 
RESTUtil.encodeNamespace(namespace));
+      }
+      return this;
+    }
+
+    public Builder setPolicyType(PolicyType policyType) {
+      Preconditions.checkArgument(policyType != null, "Policy type must be 
specified");
+      properties.put(POLICY_TYPE_CODE_KEY, 
Integer.toString(policyType.getCode()));
+      return this;
+    }
+
+    public Builder setDescription(String description) {
+      properties.put(POLICY_DESCRIPTION_KEY, description);
+      return this;
+    }
+
+    public Builder setPolicyVersion(int version) {
+      properties.put(POLICY_VERSION_KEY, Integer.toString(version));
+      return this;
+    }
+
+    public Builder setContent(String content) {
+      properties.put(POLICY_CONTENT_KEY, content);
+      return this;
+    }
+  }
+}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyType.java 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyType.java
new file mode 100644
index 00000000..029d5c37
--- /dev/null
+++ b/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyType.java
@@ -0,0 +1,84 @@
+/*
+ * 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.policy;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import jakarta.annotation.Nullable;
+
+/**
+ * Represents a policy type in Polaris. A policy type defines a category of 
policies that may be
+ * either predefined or custom (user-defined).
+ *
+ * <p>A policy type can be either inheritable or non-inheritable. Inheritable 
policies are passed
+ * down to lower-level entities (e.g., from a namespace to a table).
+ */
+public interface PolicyType {
+
+  /**
+   * Retrieves the unique type code associated with this policy type.
+   *
+   * @return the type code of the policy type
+   */
+  @JsonValue
+  int getCode();
+
+  /**
+   * Retrieves the human-readable name of this policy type.
+   *
+   * @return the name of the policy type
+   */
+  String getName();
+
+  /**
+   * Determines whether this policy type is inheritable.
+   *
+   * @return {@code true} if the policy type is inheritable, otherwise {@code 
false}
+   */
+  boolean isInheritable();
+
+  /**
+   * Retrieves a {@link PolicyType} instance corresponding to the given type 
code.
+   *
+   * <p>This method searches for the policy type in predefined policy types. 
If a custom policy type
+   * storage mechanism is implemented in the future, it may also check 
registered custom policy
+   * types.
+   *
+   * @param code the type code of the policy type
+   * @return the corresponding {@link PolicyType}, or {@code null} if no 
matching type is found
+   */
+  @JsonCreator
+  static @Nullable PolicyType fromCode(int code) {
+    return PredefinedPolicyTypes.fromCode(code);
+  }
+
+  /**
+   * Retrieves a {@link PolicyType} instance corresponding to the given policy 
name.
+   *
+   * <p>This method searches for the policy type in predefined policy types. 
If a custom policy type
+   * storage mechanism is implemented in the future, it may also check 
registered custom policy
+   * types.
+   *
+   * @param name the name of the policy type
+   * @return the corresponding {@link PolicyType}, or {@code null} if no 
matching type is found
+   */
+  static @Nullable PolicyType fromName(String name) {
+    return PredefinedPolicyTypes.fromName(name);
+  }
+}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/PredefinedPolicyTypes.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/PredefinedPolicyTypes.java
new file mode 100644
index 00000000..babe1ac7
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/PredefinedPolicyTypes.java
@@ -0,0 +1,108 @@
+/*
+ * 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.policy;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.google.common.collect.ImmutableMap;
+import jakarta.annotation.Nullable;
+
+/* Represents all predefined policy types in Polaris */
+public enum PredefinedPolicyTypes implements PolicyType {
+  DATA_COMPACTION(0, "system.data-compaction", true),
+  METADATA_COMPACTION(1, "system.metadata-compaction", true),
+  ORPHAN_FILE_REMOVAL(2, "system.orphan-file-removal", true),
+  SNAPSHOT_RETENTION(3, "system.snapshot-retention", true);
+
+  private final int code;
+  private final String name;
+  private final boolean isInheritable;
+  private static final PredefinedPolicyTypes[] REVERSE_CODE_MAPPING_ARRAY;
+  private static final ImmutableMap<String, PredefinedPolicyTypes> 
REVERSE_NAME_MAPPING_ARRAY;
+
+  static {
+    int maxId = 0;
+    for (PredefinedPolicyTypes policyType : PredefinedPolicyTypes.values()) {
+      if (maxId < policyType.code) {
+        maxId = policyType.code;
+      }
+    }
+
+    REVERSE_CODE_MAPPING_ARRAY = new PredefinedPolicyTypes[maxId + 1];
+    ImmutableMap.Builder<String, PredefinedPolicyTypes> builder = 
ImmutableMap.builder();
+    // populate both
+    for (PredefinedPolicyTypes policyType : PredefinedPolicyTypes.values()) {
+      REVERSE_CODE_MAPPING_ARRAY[policyType.code] = policyType;
+      builder.put(policyType.name, policyType);
+    }
+    REVERSE_NAME_MAPPING_ARRAY = builder.build();
+  }
+
+  PredefinedPolicyTypes(int code, String name, boolean isInheritable) {
+    this.code = code;
+    this.name = name;
+    this.isInheritable = isInheritable;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  @JsonValue
+  public int getCode() {
+    return code;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isInheritable() {
+    return isInheritable;
+  }
+
+  /**
+   * Retrieves a {@link PredefinedPolicyTypes} instance corresponding to the 
given type code.
+   *
+   * @param code the type code of the predefined policy type
+   * @return the corresponding {@link PredefinedPolicyTypes}, or {@code null} 
if no matching type is
+   *     found
+   */
+  @JsonCreator
+  public static @Nullable PredefinedPolicyTypes fromCode(int code) {
+    if (code >= REVERSE_CODE_MAPPING_ARRAY.length) {
+      return null;
+    }
+
+    return REVERSE_CODE_MAPPING_ARRAY[code];
+  }
+
+  /**
+   * Retrieves a {@link PredefinedPolicyTypes} instance corresponding to the 
given policy name.
+   *
+   * @param name the name of the predefined policy type
+   * @return the corresponding {@link PredefinedPolicyTypes}, or {@code null} 
if no matching type is
+   *     found
+   */
+  public static @Nullable PredefinedPolicyTypes fromName(String name) {
+    return REVERSE_NAME_MAPPING_ARRAY.get(name);
+  }
+}
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java
index fbcb2f9c..dd21a16f 100644
--- 
a/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java
+++ 
b/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java
@@ -105,6 +105,9 @@ public class ResolverTest {
    * - (N1/N4)
    * - N5/N6/T5
    * - N5/N6/T6
+   * - N7/N8/POL1
+   * - N7/N8/POL2
+   * - N7/POL3
    * - R1(TABLE_READ on N1/N2, VIEW_CREATE on C, TABLE_LIST on N2, TABLE_DROP 
on N5/N6/T5)
    * - R2(TABLE_WRITE_DATA on N5, VIEW_LIST on C)
    * - PR1(R1, R2)
@@ -230,6 +233,19 @@ public class ResolverTest {
         new ResolverPath(List.of("N5", "N6", "T5"), 
PolarisEntityType.TABLE_LIKE);
     this.resolveDriver(this.cache, "test", N5_N6_T5, null, null);
 
+    // N7/N8 which exists
+    ResolverPath N7_N8 = new ResolverPath(List.of("N7", "N8"), 
PolarisEntityType.NAMESPACE);
+    this.resolveDriver(this.cache, "test", N7_N8, null, null);
+
+    // N7/N8/POL1 which exists
+    ResolverPath N7_N8_POL1 =
+        new ResolverPath(List.of("N7", "N8", "POL1"), 
PolarisEntityType.POLICY);
+    this.resolveDriver(this.cache, "test", N7_N8_POL1, null, null);
+
+    // N7/POL3 which exists
+    ResolverPath N7_POL3 = new ResolverPath(List.of("N7", "POL3"), 
PolarisEntityType.POLICY);
+    this.resolveDriver(this.cache, "test", N7_POL3, null, null);
+
     // Error scenarios: N5/N6/T8 which does not exists
     ResolverPath N5_N6_T8 =
         new ResolverPath(List.of("N5", "N6", "T8"), 
PolarisEntityType.TABLE_LIKE);
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyEntityTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyEntityTest.java
new file mode 100644
index 00000000..68be34bb
--- /dev/null
+++ 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyEntityTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.policy;
+
+import java.util.stream.Stream;
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.polaris.core.entity.PolarisEntityType;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class PolicyEntityTest {
+
+  static Stream<Arguments> policyTypes() {
+    return Stream.of(
+        Arguments.of(PredefinedPolicyTypes.DATA_COMPACTION),
+        Arguments.of(PredefinedPolicyTypes.METADATA_COMPACTION),
+        Arguments.of(PredefinedPolicyTypes.ORPHAN_FILE_REMOVAL),
+        Arguments.of(PredefinedPolicyTypes.METADATA_COMPACTION));
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  public void testPolicyEntity(PolicyType policyType) {
+    PolicyEntity entity =
+        new PolicyEntity.Builder(Namespace.of("NS1"), "testPolicy", policyType)
+            .setContent("test_content")
+            .setPolicyVersion(0)
+            .build();
+    
Assertions.assertThat(entity.getType()).isEqualTo(PolarisEntityType.POLICY);
+    Assertions.assertThat(entity.getPolicyType()).isEqualTo(policyType);
+    
Assertions.assertThat(entity.getPolicyTypeCode()).isEqualTo(policyType.getCode());
+    Assertions.assertThat(entity.getContent()).isEqualTo("test_content");
+  }
+
+  @Test
+  public void testBuildPolicyEntityWithoutPolicyTye() {
+    Assertions.assertThatThrownBy(
+            () ->
+                new PolicyEntity.Builder(Namespace.of("NS1"), "testPolicy", 
null)
+                    .setContent("test_content")
+                    .setPolicyVersion(0)
+                    .build())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Policy type must be specified");
+  }
+}
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyTypeTest.java 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyTypeTest.java
new file mode 100644
index 00000000..a690cfd0
--- /dev/null
+++ 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyTypeTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.policy;
+
+import java.util.stream.Stream;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class PolicyTypeTest {
+
+  static Stream<Arguments> predefinedPolicyTypes() {
+    return Stream.of(
+        Arguments.of(0, "system.data-compaction", true),
+        Arguments.of(1, "system.metadata-compaction", true),
+        Arguments.of(2, "system.orphan-file-removal", true),
+        Arguments.of(3, "system.snapshot-retention", true));
+  }
+
+  @ParameterizedTest
+  @MethodSource("predefinedPolicyTypes")
+  public void testPredefinedPolicyTypeFromCode(int code, String name, boolean 
isInheritable) {
+    PolicyType policyType = PolicyType.fromCode(code);
+    Assertions.assertThat(policyType).isNotNull();
+    Assertions.assertThat(policyType.getCode()).isEqualTo(code);
+    Assertions.assertThat(policyType.getName()).isEqualTo(name);
+    Assertions.assertThat(policyType.isInheritable()).isEqualTo(isInheritable);
+  }
+
+  @ParameterizedTest
+  @MethodSource("predefinedPolicyTypes")
+  public void testPredefinedPolicyTypeFromName(int code, String name, boolean 
isInheritable) {
+    PolicyType policyType = PolicyType.fromName(name);
+    Assertions.assertThat(policyType).isNotNull();
+    Assertions.assertThat(policyType.getCode()).isEqualTo(code);
+    Assertions.assertThat(policyType.getName()).isEqualTo(name);
+    Assertions.assertThat(policyType.isInheritable()).isEqualTo(isInheritable);
+  }
+}
diff --git 
a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
 
b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
index 9c1d6a30..4d1927a4 100644
--- 
a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
+++ 
b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/PolarisTestMetaStoreManager.java
@@ -49,6 +49,9 @@ import 
org.apache.polaris.core.persistence.dao.entity.DropEntityResult;
 import org.apache.polaris.core.persistence.dao.entity.EntityResult;
 import org.apache.polaris.core.persistence.dao.entity.LoadGrantsResult;
 import org.apache.polaris.core.persistence.dao.entity.ResolvedEntityResult;
+import org.apache.polaris.core.policy.PolicyEntity;
+import org.apache.polaris.core.policy.PolicyType;
+import org.apache.polaris.core.policy.PredefinedPolicyTypes;
 import org.assertj.core.api.Assertions;
 
 /** Test the Polaris persistence layer */
@@ -579,12 +582,22 @@ public class PolarisTestMetaStoreManager {
       PolarisEntityType entityType,
       PolarisEntitySubType entitySubType,
       String name) {
+    return createEntity(catalogPath, entityType, entitySubType, name, null);
+  }
+
+  public PolarisBaseEntity createEntity(
+      List<PolarisEntityCore> catalogPath,
+      PolarisEntityType entityType,
+      PolarisEntitySubType entitySubType,
+      String name,
+      Map<String, String> properties) {
     return createEntity(
         catalogPath,
         entityType,
         entitySubType,
         name,
-        
polarisMetaStoreManager.generateNewEntityId(this.polarisCallContext).getId());
+        
polarisMetaStoreManager.generateNewEntityId(this.polarisCallContext).getId(),
+        properties);
   }
 
   PolarisBaseEntity createEntity(
@@ -593,6 +606,16 @@ public class PolarisTestMetaStoreManager {
       PolarisEntitySubType entitySubType,
       String name,
       long entityId) {
+    return createEntity(catalogPath, entityType, entitySubType, name, 
entityId, null);
+  }
+
+  PolarisBaseEntity createEntity(
+      List<PolarisEntityCore> catalogPath,
+      PolarisEntityType entityType,
+      PolarisEntitySubType entitySubType,
+      String name,
+      long entityId,
+      Map<String, String> properties) {
     long parentId;
     long catalogId;
     if (catalogPath != null) {
@@ -604,6 +627,7 @@ public class PolarisTestMetaStoreManager {
     }
     PolarisBaseEntity newEntity =
         new PolarisBaseEntity(catalogId, entityId, entityType, entitySubType, 
parentId, name);
+    newEntity.setPropertiesAsMap(properties);
     PolarisBaseEntity entity =
         polarisMetaStoreManager
             .createEntityIfNotExists(this.polarisCallContext, catalogPath, 
newEntity)
@@ -646,6 +670,26 @@ public class PolarisTestMetaStoreManager {
     return createEntity(catalogPath, entityType, 
PolarisEntitySubType.NULL_SUBTYPE, name);
   }
 
+  PolarisBaseEntity createEntity(
+      List<PolarisEntityCore> catalogPath,
+      PolarisEntityType entityType,
+      String name,
+      Map<String, String> properties) {
+    return createEntity(
+        catalogPath, entityType, PolarisEntitySubType.NULL_SUBTYPE, name, 
properties);
+  }
+
+  /** Create a policy entity */
+  PolicyEntity createPolicy(
+      List<PolarisEntityCore> catalogPath, String name, PolicyType policyType) 
{
+    return PolicyEntity.of(
+        createEntity(
+            catalogPath,
+            PolarisEntityType.POLICY,
+            name,
+            Map.of("policy-type-code", 
Integer.toString(policyType.getCode()))));
+  }
+
   /** Drop the entity if it exists. */
   void dropEntity(List<PolarisEntityCore> catalogPath, PolarisBaseEntity 
entityToDrop) {
     // see if the entity exists
@@ -914,7 +958,7 @@ public class PolarisTestMetaStoreManager {
 
   /**
    * Create a test catalog. This is a new catalog which will have the 
following objects (N is for a
-   * namespace, T for a table, V for a view, R for a role, P for a principal):
+   * namespace, T for a table, V for a view, R for a role, P for a principal, 
POL for a policy):
    *
    * <pre>
    * - C
@@ -927,6 +971,9 @@ public class PolarisTestMetaStoreManager {
    * - (N1/N4)
    * - N5/N6/T5
    * - N5/N6/T6
+   * - N7/N8/POL1
+   * - N7/N8/POL2
+   * - N7/POL3
    * - R1(TABLE_READ on N1/N2, VIEW_CREATE on C, TABLE_LIST on N1/N2, 
TABLE_DROP on N5/N6/T5)
    * - R2(TABLE_WRITE_DATA on N5, VIEW_LIST on C)
    * - PR1(R1, R2)
@@ -993,6 +1040,14 @@ public class PolarisTestMetaStoreManager {
         PolarisEntitySubType.TABLE,
         "T6");
 
+    PolarisBaseEntity N7 = this.createEntity(List.of(catalog), 
PolarisEntityType.NAMESPACE, "N7");
+    PolarisBaseEntity N7_N8 =
+        this.createEntity(List.of(catalog, N7), PolarisEntityType.NAMESPACE, 
"N8");
+    this.createPolicy(List.of(catalog, N7, N7_N8), "POL1", 
PredefinedPolicyTypes.DATA_COMPACTION);
+    this.createPolicy(
+        List.of(catalog, N7, N7_N8), "POL2", 
PredefinedPolicyTypes.METADATA_COMPACTION);
+    this.createPolicy(List.of(catalog, N7), "POL3", 
PredefinedPolicyTypes.SNAPSHOT_RETENTION);
+
     // the two catalog roles
     PolarisBaseEntity R1 =
         this.createEntity(List.of(catalog), PolarisEntityType.CATALOG_ROLE, 
"R1");
@@ -1659,6 +1714,17 @@ public class PolarisTestMetaStoreManager {
         PolarisEntityType.TABLE_LIKE,
         PolarisEntitySubType.TABLE,
         "T6");
+    PolarisBaseEntity N7 =
+        this.ensureExistsByName(List.of(catalog), PolarisEntityType.NAMESPACE, 
"N7");
+    PolarisBaseEntity N7_N8 =
+        this.ensureExistsByName(
+            List.of(catalog, N7),
+            PolarisEntityType.NAMESPACE,
+            PolarisEntitySubType.ANY_SUBTYPE,
+            "N8");
+    this.ensureExistsByName(List.of(catalog, N7, N7_N8), 
PolarisEntityType.POLICY, "POL1");
+    this.ensureExistsByName(List.of(catalog, N7, N7_N8), 
PolarisEntityType.POLICY, "POL2");
+    this.ensureExistsByName(List.of(catalog, N7), PolarisEntityType.POLICY, 
"POL3");
     PolarisBaseEntity R1 =
         this.ensureExistsByName(List.of(catalog), 
PolarisEntityType.CATALOG_ROLE, "R1");
     PolarisBaseEntity R2 =
@@ -1692,7 +1758,8 @@ public class PolarisTestMetaStoreManager {
         PolarisEntityType.NAMESPACE,
         List.of(
             ImmutablePair.of("N1", PolarisEntitySubType.NULL_SUBTYPE),
-            ImmutablePair.of("N5", PolarisEntitySubType.NULL_SUBTYPE)));
+            ImmutablePair.of("N5", PolarisEntitySubType.NULL_SUBTYPE),
+            ImmutablePair.of("N7", PolarisEntitySubType.NULL_SUBTYPE)));
 
     // should see 3 top-level catalog roles including the admin one
     this.validateListReturn(
@@ -1793,6 +1860,18 @@ public class PolarisTestMetaStoreManager {
                 PolarisEntitySubType.NULL_SUBTYPE),
             ImmutablePair.of("PR1", PolarisEntitySubType.NULL_SUBTYPE),
             ImmutablePair.of("PR2", PolarisEntitySubType.NULL_SUBTYPE)));
+
+    // list 2 policies under N7_N8
+    PolarisBaseEntity N7 =
+        this.ensureExistsByName(List.of(catalog), PolarisEntityType.NAMESPACE, 
"N7");
+    PolarisBaseEntity N7_N8 =
+        this.ensureExistsByName(List.of(catalog, N7), 
PolarisEntityType.NAMESPACE, "N8");
+    this.validateListReturn(
+        List.of(catalog, N7, N7_N8),
+        PolarisEntityType.POLICY,
+        List.of(
+            ImmutablePair.of("POL1", PolarisEntitySubType.NULL_SUBTYPE),
+            ImmutablePair.of("POL2", PolarisEntitySubType.NULL_SUBTYPE)));
   }
 
   /** Test that entity updates works well */
@@ -1961,6 +2040,26 @@ public class PolarisTestMetaStoreManager {
     this.dropEntity(List.of(catalog, N5), N5_N6);
     this.dropEntity(List.of(catalog), N5);
 
+    PolarisBaseEntity N7 =
+        this.ensureExistsByName(List.of(catalog), PolarisEntityType.NAMESPACE, 
"N7");
+    PolarisBaseEntity N7_N8 =
+        this.ensureExistsByName(
+            List.of(catalog, N7),
+            PolarisEntityType.NAMESPACE,
+            PolarisEntitySubType.ANY_SUBTYPE,
+            "N8");
+    PolarisBaseEntity POL1 =
+        this.ensureExistsByName(List.of(catalog, N7, N7_N8), 
PolarisEntityType.POLICY, "POL1");
+    PolarisBaseEntity POL2 =
+        this.ensureExistsByName(List.of(catalog, N7, N7_N8), 
PolarisEntityType.POLICY, "POL2");
+    PolarisBaseEntity POL3 =
+        this.ensureExistsByName(List.of(catalog, N7), 
PolarisEntityType.POLICY, "POL3");
+    this.dropEntity(List.of(catalog, N7, N7_N8), POL1);
+    this.dropEntity(List.of(catalog, N7, N7_N8), POL2);
+    this.dropEntity(List.of(catalog, N7), POL3);
+    this.dropEntity(List.of(catalog, N7), N7_N8);
+    this.dropEntity(List.of(catalog), N7);
+
     // attempt to drop the catalog again, should fail because of role R1
     this.dropEntity(null, catalog);
 

Reply via email to