This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 40aab9d43a [#13765] feat(policy): Add TMS policy content fields and
REST round-trip (#13766)
40aab9d43a is described below
commit 40aab9d43ac2cd3389e1f28265f3fc4cc61290de
Author: MaSai <[email protected]>
AuthorDate: Tue Oct 13 09:56:21 2026 +0800
[#13765] feat(policy): Add TMS policy content fields and REST round-trip
(#13766)
### What changes were proposed in this pull request?
- Register built-in policy types `system_iceberg_rewrite_manifests` and
`system_iceberg_snapshot_expiration` with placeholder content
(`trigger-expr=false`)
- Wire REST/DTO `@JsonSubTypes` and OpenAPI schemas for those types
- `PolicyContents` factories and DTO converters for the placeholders
**Out of scope (deferred):** attaching `schedule` / `minIntervalMs` /
`jobOptions` onto built-in policy content, TMS shared field models,
nearest-policy resolution, and credential-shaped option validation.
Those land in follow-up PRs once content wiring is ready.
### Why are the changes needed?
First subtask of Epic #13764, narrowed to BuiltInType registration so
TMS can map task kinds without shipping unattached public field models.
Fix: #13765
### Does this PR introduce _any_ user-facing change?
Yes. Two new built-in policy types can be created via the policies API
with empty placeholder content. They do not auto-trigger until real
thresholds land.
### How was this patch tested?
```bash
./gradlew :api:test --tests
'org.apache.gravitino.policy.TestPolicyBuiltInType' \
:common:test --tests 'org.apache.gravitino.dto.policy.TestPolicyDTO' \
--tests
'org.apache.gravitino.dto.policy.TestRewriteManifestsAndSnapshotExpirationPolicyDTO'
-PskipITs
```
---------
Co-authored-by: Cursor <[email protected]>
---
.../policy/IcebergRewriteManifestsContent.java | 90 +++++++++++++++++
.../policy/IcebergSnapshotExpirationContent.java | 90 +++++++++++++++++
.../java/org/apache/gravitino/policy/Policy.java | 9 ++
.../apache/gravitino/policy/PolicyContents.java | 18 ++++
.../gravitino/policy/TestPolicyBuiltInType.java | 18 ++++
.../gravitino/dto/policy/PolicyContentDTO.java | 78 +++++++++++++++
.../org/apache/gravitino/dto/policy/PolicyDTO.java | 6 ++
.../dto/requests/PolicyCreateRequest.java | 6 ++
.../dto/requests/PolicyUpdateRequest.java | 6 ++
.../apache/gravitino/dto/util/DTOConverters.java | 14 +++
...iteManifestsAndSnapshotExpirationPolicyDTO.java | 68 +++++++++++++
.../org/apache/gravitino/policy/PolicyManager.java | 4 +
docs/open-api/policies.yaml | 109 ++++++++++++++++++++-
13 files changed, 512 insertions(+), 4 deletions(-)
diff --git
a/api/src/main/java/org/apache/gravitino/policy/IcebergRewriteManifestsContent.java
b/api/src/main/java/org/apache/gravitino/policy/IcebergRewriteManifestsContent.java
new file mode 100644
index 0000000000..53d59202cb
--- /dev/null
+++
b/api/src/main/java/org/apache/gravitino/policy/IcebergRewriteManifestsContent.java
@@ -0,0 +1,90 @@
+/*
+ * 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.gravitino.policy;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import org.apache.gravitino.MetadataObject;
+
+/**
+ * Placeholder built-in policy content for Iceberg manifest rewrite.
+ *
+ * <p>Configurable thresholds will be added when the rewrite-manifests policy
lands; this class
+ * registers the built-in type for TMS task mapping.
+ */
+public class IcebergRewriteManifestsContent implements PolicyContent {
+ /** Property key for strategy type. */
+ public static final String STRATEGY_TYPE_KEY = "strategy.type";
+ /** Property key for the job template. */
+ public static final String JOB_TEMPLATE_NAME_KEY = "job.template-name";
+ /** Rule key for the trigger expression. */
+ public static final String TRIGGER_EXPR_KEY = "trigger-expr";
+ /** Rule key for the score expression. */
+ public static final String SCORE_EXPR_KEY = "score-expr";
+ /** Strategy type for manifest rewrite. */
+ public static final String STRATEGY_TYPE_VALUE = "iceberg-rewrite-manifests";
+ /** Built-in job template name for rewrite manifests. */
+ public static final String JOB_TEMPLATE_NAME_VALUE =
"builtin-iceberg-rewrite-manifests";
+
+ private static final Set<MetadataObject.Type> SUPPORTED_OBJECT_TYPES =
+ ImmutableSet.of(
+ MetadataObject.Type.CATALOG, MetadataObject.Type.SCHEMA,
MetadataObject.Type.TABLE);
+
+ /** Default constructor for Jackson deserialization only. */
+ public IcebergRewriteManifestsContent() {}
+
+ @Override
+ public Set<MetadataObject.Type> supportedObjectTypes() {
+ return SUPPORTED_OBJECT_TYPES;
+ }
+
+ @Override
+ public Map<String, String> properties() {
+ return ImmutableMap.of(
+ STRATEGY_TYPE_KEY, STRATEGY_TYPE_VALUE, JOB_TEMPLATE_NAME_KEY,
JOB_TEMPLATE_NAME_VALUE);
+ }
+
+ @Override
+ public Map<String, Object> rules() {
+ Map<String, Object> rules = new LinkedHashMap<>();
+ // Placeholders must not auto-trigger until real thresholds land in a
follow-up.
+ rules.put(TRIGGER_EXPR_KEY, "false");
+ rules.put(SCORE_EXPR_KEY, "1");
+ return Collections.unmodifiableMap(rules);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof IcebergRewriteManifestsContent;
+ }
+
+ @Override
+ public int hashCode() {
+ return IcebergRewriteManifestsContent.class.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "IcebergRewriteManifestsContent{}";
+ }
+}
diff --git
a/api/src/main/java/org/apache/gravitino/policy/IcebergSnapshotExpirationContent.java
b/api/src/main/java/org/apache/gravitino/policy/IcebergSnapshotExpirationContent.java
new file mode 100644
index 0000000000..d752d8f905
--- /dev/null
+++
b/api/src/main/java/org/apache/gravitino/policy/IcebergSnapshotExpirationContent.java
@@ -0,0 +1,90 @@
+/*
+ * 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.gravitino.policy;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import org.apache.gravitino.MetadataObject;
+
+/**
+ * Placeholder built-in policy content for Iceberg snapshot expiration.
+ *
+ * <p>Configurable thresholds will be added when the expire-snapshots policy
lands; this class
+ * registers the built-in type for TMS task mapping.
+ */
+public class IcebergSnapshotExpirationContent implements PolicyContent {
+ /** Property key for strategy type. */
+ public static final String STRATEGY_TYPE_KEY = "strategy.type";
+ /** Property key for the job template. */
+ public static final String JOB_TEMPLATE_NAME_KEY = "job.template-name";
+ /** Rule key for the trigger expression. */
+ public static final String TRIGGER_EXPR_KEY = "trigger-expr";
+ /** Rule key for the score expression. */
+ public static final String SCORE_EXPR_KEY = "score-expr";
+ /** Strategy type for snapshot expiration. */
+ public static final String STRATEGY_TYPE_VALUE =
"iceberg-snapshot-expiration";
+ /** Built-in job template name for expire snapshots. */
+ public static final String JOB_TEMPLATE_NAME_VALUE =
"builtin-iceberg-expire-snapshots";
+
+ private static final Set<MetadataObject.Type> SUPPORTED_OBJECT_TYPES =
+ ImmutableSet.of(
+ MetadataObject.Type.CATALOG, MetadataObject.Type.SCHEMA,
MetadataObject.Type.TABLE);
+
+ /** Default constructor for Jackson deserialization only. */
+ public IcebergSnapshotExpirationContent() {}
+
+ @Override
+ public Set<MetadataObject.Type> supportedObjectTypes() {
+ return SUPPORTED_OBJECT_TYPES;
+ }
+
+ @Override
+ public Map<String, String> properties() {
+ return ImmutableMap.of(
+ STRATEGY_TYPE_KEY, STRATEGY_TYPE_VALUE, JOB_TEMPLATE_NAME_KEY,
JOB_TEMPLATE_NAME_VALUE);
+ }
+
+ @Override
+ public Map<String, Object> rules() {
+ Map<String, Object> rules = new LinkedHashMap<>();
+ // Placeholders must not auto-trigger until real thresholds land in a
follow-up.
+ rules.put(TRIGGER_EXPR_KEY, "false");
+ rules.put(SCORE_EXPR_KEY, "1");
+ return Collections.unmodifiableMap(rules);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof IcebergSnapshotExpirationContent;
+ }
+
+ @Override
+ public int hashCode() {
+ return IcebergSnapshotExpirationContent.class.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "IcebergSnapshotExpirationContent{}";
+ }
+}
diff --git a/api/src/main/java/org/apache/gravitino/policy/Policy.java
b/api/src/main/java/org/apache/gravitino/policy/Policy.java
index 80519d1157..c71a9c213b 100644
--- a/api/src/main/java/org/apache/gravitino/policy/Policy.java
+++ b/api/src/main/java/org/apache/gravitino/policy/Policy.java
@@ -47,6 +47,15 @@ public interface Policy extends Auditable {
BUILT_IN_TYPE_PREFIX + "iceberg_orphan_file_removal",
IcebergOrphanFileRemovalContent.class),
+ /** Iceberg manifest rewrite policy. */
+ ICEBERG_REWRITE_MANIFESTS(
+ BUILT_IN_TYPE_PREFIX + "iceberg_rewrite_manifests",
IcebergRewriteManifestsContent.class),
+
+ /** Iceberg snapshot expiration policy. */
+ ICEBERG_SNAPSHOT_EXPIRATION(
+ BUILT_IN_TYPE_PREFIX + "iceberg_snapshot_expiration",
+ IcebergSnapshotExpirationContent.class),
+
/** Built-in policy type for filtering rows of tagged tables. */
ROW_FILTER(BUILT_IN_TYPE_PREFIX + "row_filter", RowFilterContent.class),
diff --git a/api/src/main/java/org/apache/gravitino/policy/PolicyContents.java
b/api/src/main/java/org/apache/gravitino/policy/PolicyContents.java
index 501475c51f..8ca63b1f85 100644
--- a/api/src/main/java/org/apache/gravitino/policy/PolicyContents.java
+++ b/api/src/main/java/org/apache/gravitino/policy/PolicyContents.java
@@ -168,6 +168,24 @@ public class PolicyContents {
return new IcebergOrphanFileRemovalContent(olderThanDays, location,
dryRun);
}
+ /**
+ * Creates placeholder Iceberg rewrite-manifests policy content.
+ *
+ * @return rewrite-manifests policy content
+ */
+ public static PolicyContent icebergRewriteManifests() {
+ return new IcebergRewriteManifestsContent();
+ }
+
+ /**
+ * Creates placeholder Iceberg snapshot-expiration policy content.
+ *
+ * @return snapshot-expiration policy content
+ */
+ public static PolicyContent icebergSnapshotExpiration() {
+ return new IcebergSnapshotExpirationContent();
+ }
+
/**
* Creates a row-filter policy for tagged tables.
*
diff --git
a/api/src/test/java/org/apache/gravitino/policy/TestPolicyBuiltInType.java
b/api/src/test/java/org/apache/gravitino/policy/TestPolicyBuiltInType.java
index 9e49f61326..93df4a1d6a 100644
--- a/api/src/test/java/org/apache/gravitino/policy/TestPolicyBuiltInType.java
+++ b/api/src/test/java/org/apache/gravitino/policy/TestPolicyBuiltInType.java
@@ -29,6 +29,12 @@ public class TestPolicyBuiltInType {
Assertions.assertEquals(
Policy.BuiltInType.ICEBERG_COMPACTION,
Policy.BuiltInType.fromPolicyType("system_iceberg_compaction"));
+ Assertions.assertEquals(
+ Policy.BuiltInType.ICEBERG_REWRITE_MANIFESTS,
+ Policy.BuiltInType.fromPolicyType("system_iceberg_rewrite_manifests"));
+ Assertions.assertEquals(
+ Policy.BuiltInType.ICEBERG_SNAPSHOT_EXPIRATION,
+
Policy.BuiltInType.fromPolicyType("system_iceberg_snapshot_expiration"));
Assertions.assertEquals(
Policy.BuiltInType.ROW_FILTER,
Policy.BuiltInType.fromPolicyType("system_row_filter"));
Assertions.assertEquals(
@@ -55,6 +61,12 @@ public class TestPolicyBuiltInType {
void testBuiltInTypePolicyTypeValue() {
Assertions.assertEquals(
"system_iceberg_compaction",
Policy.BuiltInType.ICEBERG_COMPACTION.policyType());
+ Assertions.assertEquals(
+ "system_iceberg_rewrite_manifests",
+ Policy.BuiltInType.ICEBERG_REWRITE_MANIFESTS.policyType());
+ Assertions.assertEquals(
+ "system_iceberg_snapshot_expiration",
+ Policy.BuiltInType.ICEBERG_SNAPSHOT_EXPIRATION.policyType());
Assertions.assertEquals("system_row_filter",
Policy.BuiltInType.ROW_FILTER.policyType());
Assertions.assertEquals("system_column_mask",
Policy.BuiltInType.COLUMN_MASK.policyType());
}
@@ -63,6 +75,12 @@ public class TestPolicyBuiltInType {
void testBuiltInTypeContentClass() {
Assertions.assertEquals(
IcebergDataCompactionContent.class,
Policy.BuiltInType.ICEBERG_COMPACTION.contentClass());
+ Assertions.assertEquals(
+ IcebergRewriteManifestsContent.class,
+ Policy.BuiltInType.ICEBERG_REWRITE_MANIFESTS.contentClass());
+ Assertions.assertEquals(
+ IcebergSnapshotExpirationContent.class,
+ Policy.BuiltInType.ICEBERG_SNAPSHOT_EXPIRATION.contentClass());
Assertions.assertEquals(RowFilterContent.class,
Policy.BuiltInType.ROW_FILTER.contentClass());
Assertions.assertEquals(ColumnMaskContent.class,
Policy.BuiltInType.COLUMN_MASK.contentClass());
}
diff --git
a/common/src/main/java/org/apache/gravitino/dto/policy/PolicyContentDTO.java
b/common/src/main/java/org/apache/gravitino/dto/policy/PolicyContentDTO.java
index 65fae911ad..4ac19d7be4 100644
--- a/common/src/main/java/org/apache/gravitino/dto/policy/PolicyContentDTO.java
+++ b/common/src/main/java/org/apache/gravitino/dto/policy/PolicyContentDTO.java
@@ -304,6 +304,84 @@ public interface PolicyContentDTO extends PolicyContent {
}
}
+ /** Placeholder DTO for Iceberg rewrite-manifests policy content. */
+ @EqualsAndHashCode
+ @ToString
+ class IcebergRewriteManifestsContentDTO implements PolicyContentDTO {
+ private IcebergRewriteManifestsContentDTO() {}
+
+ /**
+ * @return empty rewrite-manifests content DTO
+ */
+ public static IcebergRewriteManifestsContentDTO instance() {
+ return new IcebergRewriteManifestsContentDTO();
+ }
+
+ @Override
+ public Set<MetadataObject.Type> supportedObjectTypes() {
+ return toDomainContent().supportedObjectTypes();
+ }
+
+ @Override
+ public Map<String, String> properties() {
+ return toDomainContent().properties();
+ }
+
+ @Override
+ public Map<String, Object> rules() {
+ return toDomainContent().rules();
+ }
+
+ @Override
+ public void validate() {
+ PolicyContentDTO.super.validate();
+ toDomainContent().validate();
+ }
+
+ private PolicyContent toDomainContent() {
+ return PolicyContents.icebergRewriteManifests();
+ }
+ }
+
+ /** Placeholder DTO for Iceberg snapshot-expiration policy content. */
+ @EqualsAndHashCode
+ @ToString
+ class IcebergSnapshotExpirationContentDTO implements PolicyContentDTO {
+ private IcebergSnapshotExpirationContentDTO() {}
+
+ /**
+ * @return empty snapshot-expiration content DTO
+ */
+ public static IcebergSnapshotExpirationContentDTO instance() {
+ return new IcebergSnapshotExpirationContentDTO();
+ }
+
+ @Override
+ public Set<MetadataObject.Type> supportedObjectTypes() {
+ return toDomainContent().supportedObjectTypes();
+ }
+
+ @Override
+ public Map<String, String> properties() {
+ return toDomainContent().properties();
+ }
+
+ @Override
+ public Map<String, Object> rules() {
+ return toDomainContent().rules();
+ }
+
+ @Override
+ public void validate() {
+ PolicyContentDTO.super.validate();
+ toDomainContent().validate();
+ }
+
+ private PolicyContent toDomainContent() {
+ return PolicyContents.icebergSnapshotExpiration();
+ }
+ }
+
/** Represents typed row-filter policy content. */
@EqualsAndHashCode
@ToString
diff --git
a/common/src/main/java/org/apache/gravitino/dto/policy/PolicyDTO.java
b/common/src/main/java/org/apache/gravitino/dto/policy/PolicyDTO.java
index 2f75784868..8ceff3a327 100644
--- a/common/src/main/java/org/apache/gravitino/dto/policy/PolicyDTO.java
+++ b/common/src/main/java/org/apache/gravitino/dto/policy/PolicyDTO.java
@@ -61,6 +61,12 @@ public class PolicyDTO implements Policy {
@JsonSubTypes.Type(
value = PolicyContentDTO.IcebergOrphanFileRemovalContentDTO.class,
name = "system_iceberg_orphan_file_removal"),
+ @JsonSubTypes.Type(
+ value = PolicyContentDTO.IcebergRewriteManifestsContentDTO.class,
+ name = "system_iceberg_rewrite_manifests"),
+ @JsonSubTypes.Type(
+ value = PolicyContentDTO.IcebergSnapshotExpirationContentDTO.class,
+ name = "system_iceberg_snapshot_expiration"),
@JsonSubTypes.Type(
value = PolicyContentDTO.RowFilterContentDTO.class,
name = "system_row_filter"),
diff --git
a/common/src/main/java/org/apache/gravitino/dto/requests/PolicyCreateRequest.java
b/common/src/main/java/org/apache/gravitino/dto/requests/PolicyCreateRequest.java
index d5ef0430a7..96a2357289 100644
---
a/common/src/main/java/org/apache/gravitino/dto/requests/PolicyCreateRequest.java
+++
b/common/src/main/java/org/apache/gravitino/dto/requests/PolicyCreateRequest.java
@@ -65,6 +65,12 @@ public class PolicyCreateRequest implements RESTRequest {
@JsonSubTypes.Type(
value = PolicyContentDTO.IcebergOrphanFileRemovalContentDTO.class,
name = "system_iceberg_orphan_file_removal"),
+ @JsonSubTypes.Type(
+ value = PolicyContentDTO.IcebergRewriteManifestsContentDTO.class,
+ name = "system_iceberg_rewrite_manifests"),
+ @JsonSubTypes.Type(
+ value = PolicyContentDTO.IcebergSnapshotExpirationContentDTO.class,
+ name = "system_iceberg_snapshot_expiration"),
@JsonSubTypes.Type(
value = PolicyContentDTO.RowFilterContentDTO.class,
name = "system_row_filter"),
diff --git
a/common/src/main/java/org/apache/gravitino/dto/requests/PolicyUpdateRequest.java
b/common/src/main/java/org/apache/gravitino/dto/requests/PolicyUpdateRequest.java
index 28a37aa25f..265296110b 100644
---
a/common/src/main/java/org/apache/gravitino/dto/requests/PolicyUpdateRequest.java
+++
b/common/src/main/java/org/apache/gravitino/dto/requests/PolicyUpdateRequest.java
@@ -146,6 +146,12 @@ public interface PolicyUpdateRequest extends RESTRequest {
@JsonSubTypes.Type(
value = PolicyContentDTO.IcebergOrphanFileRemovalContentDTO.class,
name = "system_iceberg_orphan_file_removal"),
+ @JsonSubTypes.Type(
+ value = PolicyContentDTO.IcebergRewriteManifestsContentDTO.class,
+ name = "system_iceberg_rewrite_manifests"),
+ @JsonSubTypes.Type(
+ value = PolicyContentDTO.IcebergSnapshotExpirationContentDTO.class,
+ name = "system_iceberg_snapshot_expiration"),
@JsonSubTypes.Type(
value = PolicyContentDTO.RowFilterContentDTO.class,
name = "system_row_filter"),
diff --git
a/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
b/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
index fd45252a6a..ebf54be030 100644
--- a/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
+++ b/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java
@@ -107,6 +107,8 @@ import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.policy.ColumnMaskContent;
import org.apache.gravitino.policy.IcebergDataCompactionContent;
import org.apache.gravitino.policy.IcebergOrphanFileRemovalContent;
+import org.apache.gravitino.policy.IcebergRewriteManifestsContent;
+import org.apache.gravitino.policy.IcebergSnapshotExpirationContent;
import org.apache.gravitino.policy.PolicyContent;
import org.apache.gravitino.policy.PolicyContents;
import org.apache.gravitino.policy.RowFilterContent;
@@ -686,6 +688,12 @@ public class DTOConverters {
.withDryRun(content.dryRun())
.build();
}
+ if (policyContent instanceof IcebergRewriteManifestsContent) {
+ return PolicyContentDTO.IcebergRewriteManifestsContentDTO.instance();
+ }
+ if (policyContent instanceof IcebergSnapshotExpirationContent) {
+ return PolicyContentDTO.IcebergSnapshotExpirationContentDTO.instance();
+ }
if (policyContent instanceof IcebergDataCompactionContent) {
IcebergDataCompactionContent icebergCompactionContent =
(IcebergDataCompactionContent) policyContent;
@@ -1565,6 +1573,12 @@ public class DTOConverters {
return PolicyContents.icebergOrphanFileRemoval(
content.olderThanDays(), content.location(), content.dryRun());
}
+ if (policyContentDTO instanceof
PolicyContentDTO.IcebergRewriteManifestsContentDTO) {
+ return PolicyContents.icebergRewriteManifests();
+ }
+ if (policyContentDTO instanceof
PolicyContentDTO.IcebergSnapshotExpirationContentDTO) {
+ return PolicyContents.icebergSnapshotExpiration();
+ }
if (policyContentDTO instanceof
PolicyContentDTO.IcebergCompactionContentDTO) {
PolicyContentDTO.IcebergCompactionContentDTO icebergCompactionContentDTO
=
(PolicyContentDTO.IcebergCompactionContentDTO) policyContentDTO;
diff --git
a/common/src/test/java/org/apache/gravitino/dto/policy/TestRewriteManifestsAndSnapshotExpirationPolicyDTO.java
b/common/src/test/java/org/apache/gravitino/dto/policy/TestRewriteManifestsAndSnapshotExpirationPolicyDTO.java
new file mode 100644
index 0000000000..2087eef48a
--- /dev/null
+++
b/common/src/test/java/org/apache/gravitino/dto/policy/TestRewriteManifestsAndSnapshotExpirationPolicyDTO.java
@@ -0,0 +1,68 @@
+/*
+ * 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.gravitino.dto.policy;
+
+import org.apache.gravitino.dto.requests.PolicyCreateRequest;
+import org.apache.gravitino.dto.requests.PolicyUpdateRequest;
+import org.apache.gravitino.dto.util.DTOConverters;
+import org.apache.gravitino.json.JsonUtils;
+import org.apache.gravitino.policy.PolicyContent;
+import org.apache.gravitino.policy.PolicyContents;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class TestRewriteManifestsAndSnapshotExpirationPolicyDTO {
+
+ @Test
+ void rewriteManifestsCreateAndUpdateRequestTypedContent() throws Exception {
+ assertCreateAndUpdateRoundTrip(
+ "system_iceberg_rewrite_manifests",
PolicyContents.icebergRewriteManifests());
+ }
+
+ @Test
+ void snapshotExpirationCreateAndUpdateRequestTypedContent() throws Exception
{
+ assertCreateAndUpdateRoundTrip(
+ "system_iceberg_snapshot_expiration",
PolicyContents.icebergSnapshotExpiration());
+ }
+
+ private static void assertCreateAndUpdateRoundTrip(String policyType,
PolicyContent expected)
+ throws Exception {
+ PolicyCreateRequest create =
+ JsonUtils.objectMapper()
+ .readValue(
+ "{\"name\":\"p\",\"policyType\":\""
+ + policyType
+ + "\",\"enabled\":true,\"content\":{}}",
+ PolicyCreateRequest.class);
+ create.validate();
+ Assertions.assertEquals(expected,
DTOConverters.fromDTO(create.getPolicyContent()));
+
+ PolicyUpdateRequest update =
+ JsonUtils.objectMapper()
+ .readValue(
+ "{\"@type\":\"updateContent\",\"policyType\":\""
+ + policyType
+ + "\",\"newContent\":{}}",
+ PolicyUpdateRequest.class);
+ update.validate();
+ PolicyUpdateRequest.UpdatePolicyContentRequest contentUpdate =
+ (PolicyUpdateRequest.UpdatePolicyContentRequest) update;
+ Assertions.assertEquals(expected,
DTOConverters.fromDTO(contentUpdate.getNewContent()));
+ }
+}
diff --git a/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
b/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
index b742ef98e7..5c10ac000e 100644
--- a/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
+++ b/core/src/main/java/org/apache/gravitino/policy/PolicyManager.java
@@ -115,6 +115,8 @@ public class PolicyManager implements PolicyDispatcher {
throws PolicyAlreadyExistsException {
NameIdentifier metalakeIdent = NameIdentifierUtil.ofMetalake(metalake);
checkMetalake(metalakeIdent, entityStore);
+ content.validate();
+
PolicyEntity policyEntity =
PolicyEntity.builder()
.withId(idGenerator.nextId())
@@ -338,6 +340,8 @@ public class PolicyManager implements PolicyDispatcher {
}
}
+ newContent.validate();
+
PolicyEntity.Builder builder = newPolicyBuilder(policyEntity);
builder.withName(newName);
builder.withComment(newComment);
diff --git a/docs/open-api/policies.yaml b/docs/open-api/policies.yaml
index 3a1a943fb3..c8089274f0 100644
--- a/docs/open-api/policies.yaml
+++ b/docs/open-api/policies.yaml
@@ -336,7 +336,7 @@ components:
IcebergCompactionContent:
type: object
- description: Built-in Iceberg compaction thresholds, scoring weights,
and job options.
+ description: Built-in Iceberg compaction thresholds, scoring weights,
and rewrite options.
properties:
minDataFileMse:
type: integer
@@ -363,6 +363,15 @@ components:
format: int64
minimum: 1
default: 50
+ rewriteStrategy:
+ type: string
+ enum: [binpack, sort]
+ default: binpack
+ description: Iceberg rewrite_data_files top-level strategy.
+ sortOrder:
+ type: string
+ default: ""
+ description: Iceberg rewrite_data_files top-level sort_order.
Required when rewriteStrategy is sort; must be empty for binpack.
rewriteOptions:
type: object
default: {}
@@ -439,6 +448,64 @@ components:
content:
$ref: '#/components/schemas/IcebergOrphanFileRemovalContent'
+ IcebergRewriteManifestsContent:
+ type: object
+ description: Placeholder content for system_iceberg_rewrite_manifests.
Configurable thresholds land in a follow-up.
+ additionalProperties: false
+
+ IcebergRewriteManifestsPolicy:
+ allOf:
+ - $ref: '#/components/schemas/PolicyBase'
+ - type: object
+ required: [content]
+ properties:
+ policyType:
+ type: string
+ enum: [system_iceberg_rewrite_manifests]
+ content:
+ $ref: '#/components/schemas/IcebergRewriteManifestsContent'
+
+ IcebergRewriteManifestsPolicyCreateRequest:
+ allOf:
+ - $ref: '#/components/schemas/PolicyCreateRequestBase'
+ - type: object
+ required: [content]
+ properties:
+ policyType:
+ type: string
+ enum: [system_iceberg_rewrite_manifests]
+ content:
+ $ref: '#/components/schemas/IcebergRewriteManifestsContent'
+
+ IcebergSnapshotExpirationContent:
+ type: object
+ description: Placeholder content for system_iceberg_snapshot_expiration.
Configurable thresholds land in a follow-up.
+ additionalProperties: false
+
+ IcebergSnapshotExpirationPolicy:
+ allOf:
+ - $ref: '#/components/schemas/PolicyBase'
+ - type: object
+ required: [content]
+ properties:
+ policyType:
+ type: string
+ enum: [system_iceberg_snapshot_expiration]
+ content:
+ $ref: '#/components/schemas/IcebergSnapshotExpirationContent'
+
+ IcebergSnapshotExpirationPolicyCreateRequest:
+ allOf:
+ - $ref: '#/components/schemas/PolicyCreateRequestBase'
+ - type: object
+ required: [content]
+ properties:
+ policyType:
+ type: string
+ enum: [system_iceberg_snapshot_expiration]
+ content:
+ $ref: '#/components/schemas/IcebergSnapshotExpirationContent'
+
RowFilterContent:
type: object
description: Expression content for a built-in row-filter policy.
@@ -593,7 +660,7 @@ components:
policyType:
type: string
not:
- enum: [system_iceberg_compaction,
system_iceberg_orphan_file_removal, system_row_filter, system_column_mask]
+ enum: [system_iceberg_compaction,
system_iceberg_orphan_file_removal, system_iceberg_rewrite_manifests,
system_iceberg_snapshot_expiration, system_row_filter, system_column_mask]
content:
$ref: '#/components/schemas/CustomPolicyContent'
@@ -604,6 +671,8 @@ components:
- $ref: '#/components/schemas/CustomPolicy'
- $ref: '#/components/schemas/IcebergOrphanFileRemovalPolicy'
- $ref: '#/components/schemas/IcebergCompactionPolicy'
+ - $ref: '#/components/schemas/IcebergRewriteManifestsPolicy'
+ - $ref: '#/components/schemas/IcebergSnapshotExpirationPolicy'
- $ref: '#/components/schemas/RowFilterPolicy'
- $ref: '#/components/schemas/ColumnMaskPolicy'
discriminator:
@@ -612,6 +681,8 @@ components:
custom: '#/components/schemas/CustomPolicy'
system_iceberg_orphan_file_removal:
'#/components/schemas/IcebergOrphanFileRemovalPolicy'
system_iceberg_compaction:
'#/components/schemas/IcebergCompactionPolicy'
+ system_iceberg_rewrite_manifests:
'#/components/schemas/IcebergRewriteManifestsPolicy'
+ system_iceberg_snapshot_expiration:
'#/components/schemas/IcebergSnapshotExpirationPolicy'
system_row_filter: '#/components/schemas/RowFilterPolicy'
system_column_mask: '#/components/schemas/ColumnMaskPolicy'
@@ -684,7 +755,7 @@ components:
policyType:
type: string
not:
- enum: [system_iceberg_compaction,
system_iceberg_orphan_file_removal, system_row_filter, system_column_mask]
+ enum: [system_iceberg_compaction,
system_iceberg_orphan_file_removal, system_iceberg_rewrite_manifests,
system_iceberg_snapshot_expiration, system_row_filter, system_column_mask]
content:
$ref: '#/components/schemas/CustomPolicyContent'
@@ -694,6 +765,8 @@ components:
oneOf:
- $ref: '#/components/schemas/CustomPolicyCreateRequest'
- $ref:
'#/components/schemas/IcebergOrphanFileRemovalPolicyCreateRequest'
+ - $ref:
'#/components/schemas/IcebergRewriteManifestsPolicyCreateRequest'
+ - $ref:
'#/components/schemas/IcebergSnapshotExpirationPolicyCreateRequest'
- $ref: '#/components/schemas/IcebergCompactionPolicyCreateRequest'
- $ref: '#/components/schemas/RowFilterPolicyCreateRequest'
- $ref: '#/components/schemas/ColumnMaskPolicyCreateRequest'
@@ -702,6 +775,8 @@ components:
mapping:
custom: '#/components/schemas/CustomPolicyCreateRequest'
system_iceberg_orphan_file_removal:
'#/components/schemas/IcebergOrphanFileRemovalPolicyCreateRequest'
+ system_iceberg_rewrite_manifests:
'#/components/schemas/IcebergRewriteManifestsPolicyCreateRequest'
+ system_iceberg_snapshot_expiration:
'#/components/schemas/IcebergSnapshotExpirationPolicyCreateRequest'
system_iceberg_compaction:
'#/components/schemas/IcebergCompactionPolicyCreateRequest'
system_row_filter:
'#/components/schemas/RowFilterPolicyCreateRequest'
system_column_mask:
'#/components/schemas/ColumnMaskPolicyCreateRequest'
@@ -794,7 +869,7 @@ components:
policyType:
type: string
not:
- enum: [system_iceberg_compaction,
system_iceberg_orphan_file_removal, system_row_filter, system_column_mask]
+ enum: [system_iceberg_compaction,
system_iceberg_orphan_file_removal, system_iceberg_rewrite_manifests,
system_iceberg_snapshot_expiration, system_row_filter, system_column_mask]
newContent:
$ref: '#/components/schemas/CustomPolicyContent'
@@ -820,6 +895,28 @@ components:
newContent:
$ref: '#/components/schemas/IcebergOrphanFileRemovalContent'
+ UpdateIcebergRewriteManifestsPolicyContentRequest:
+ allOf:
+ - $ref: '#/components/schemas/UpdatePolicyContentRequestBase'
+ - type: object
+ properties:
+ policyType:
+ type: string
+ enum: [system_iceberg_rewrite_manifests]
+ newContent:
+ $ref: '#/components/schemas/IcebergRewriteManifestsContent'
+
+ UpdateIcebergSnapshotExpirationPolicyContentRequest:
+ allOf:
+ - $ref: '#/components/schemas/UpdatePolicyContentRequestBase'
+ - type: object
+ properties:
+ policyType:
+ type: string
+ enum: [system_iceberg_snapshot_expiration]
+ newContent:
+ $ref: '#/components/schemas/IcebergSnapshotExpirationContent'
+
UpdateRowFilterPolicyContentRequest:
allOf:
- $ref: '#/components/schemas/UpdatePolicyContentRequestBase'
@@ -848,6 +945,8 @@ components:
- $ref: '#/components/schemas/UpdateCustomPolicyContentRequest'
- $ref:
'#/components/schemas/UpdateIcebergCompactionPolicyContentRequest'
- $ref:
'#/components/schemas/UpdateIcebergOrphanFileRemovalPolicyContentRequest'
+ - $ref:
'#/components/schemas/UpdateIcebergRewriteManifestsPolicyContentRequest'
+ - $ref:
'#/components/schemas/UpdateIcebergSnapshotExpirationPolicyContentRequest'
- $ref: '#/components/schemas/UpdateRowFilterPolicyContentRequest'
- $ref: '#/components/schemas/UpdateColumnMaskPolicyContentRequest'
discriminator:
@@ -856,6 +955,8 @@ components:
custom: '#/components/schemas/UpdateCustomPolicyContentRequest'
system_iceberg_compaction:
'#/components/schemas/UpdateIcebergCompactionPolicyContentRequest'
system_iceberg_orphan_file_removal:
'#/components/schemas/UpdateIcebergOrphanFileRemovalPolicyContentRequest'
+ system_iceberg_rewrite_manifests:
'#/components/schemas/UpdateIcebergRewriteManifestsPolicyContentRequest'
+ system_iceberg_snapshot_expiration:
'#/components/schemas/UpdateIcebergSnapshotExpirationPolicyContentRequest'
system_row_filter:
'#/components/schemas/UpdateRowFilterPolicyContentRequest'
system_column_mask:
'#/components/schemas/UpdateColumnMaskPolicyContentRequest'
example: {