adutra commented on code in PR #3293: URL: https://github.com/apache/polaris/pull/3293#discussion_r2665275658
########## runtime/service/src/main/java/org/apache/polaris/service/events/AllowedAttributeTypes.java: ########## @@ -0,0 +1,134 @@ +/* + * 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.events; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.iceberg.TableMetadata; +import org.apache.iceberg.catalog.Namespace; +import org.apache.iceberg.catalog.TableIdentifier; +import org.apache.iceberg.rest.requests.CommitTransactionRequest; +import org.apache.iceberg.rest.requests.CreateNamespaceRequest; +import org.apache.iceberg.rest.requests.CreateTableRequest; +import org.apache.iceberg.rest.requests.CreateViewRequest; +import org.apache.iceberg.rest.requests.RegisterTableRequest; +import org.apache.iceberg.rest.requests.RenameTableRequest; +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.LoadTableResponse; +import org.apache.iceberg.rest.responses.LoadViewResponse; +import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse; +import org.apache.iceberg.view.ViewMetadata; +import org.apache.polaris.core.admin.model.AddGrantRequest; +import org.apache.polaris.core.admin.model.Catalog; +import org.apache.polaris.core.admin.model.CatalogRole; +import org.apache.polaris.core.admin.model.CreatePrincipalRequest; +import org.apache.polaris.core.admin.model.CreatePrincipalRoleRequest; +import org.apache.polaris.core.admin.model.GrantResource; +import org.apache.polaris.core.admin.model.Principal; +import org.apache.polaris.core.admin.model.PrincipalRole; +import org.apache.polaris.core.admin.model.PrincipalWithCredentials; +import org.apache.polaris.core.admin.model.RevokeGrantRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRoleRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; +import org.apache.polaris.core.entity.PolarisPrivilege; +import org.apache.polaris.service.types.AttachPolicyRequest; +import org.apache.polaris.service.types.CommitViewRequest; +import org.apache.polaris.service.types.CreateGenericTableRequest; +import org.apache.polaris.service.types.CreatePolicyRequest; +import org.apache.polaris.service.types.DetachPolicyRequest; +import org.apache.polaris.service.types.GenericTable; +import org.apache.polaris.service.types.GetApplicablePoliciesResponse; +import org.apache.polaris.service.types.LoadPolicyResponse; +import org.apache.polaris.service.types.NotificationRequest; +import org.apache.polaris.service.types.UpdatePolicyRequest; + +/** Whitelist of types allowed for event attributes. */ +final class AllowedAttributeTypes { + private AllowedAttributeTypes() {} + + static final Set<Class<?>> ALLOWED_TYPES = + Set.of( + // Primitives + String.class, + Boolean.class, + Integer.class, + Long.class, + Double.class, + // Collections + List.class, Review Comment: This is probably too loose. We are not checking the collections element types here, so one could include a list or a set of any element type, effectively beating the purpose of this class in the first place. I would suggest to also check the element type and the map's key and value types: these should conform to the list specified here as well. ########## runtime/service/src/main/java/org/apache/polaris/service/events/AllowedAttributeTypes.java: ########## @@ -0,0 +1,134 @@ +/* + * 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.events; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.iceberg.TableMetadata; +import org.apache.iceberg.catalog.Namespace; +import org.apache.iceberg.catalog.TableIdentifier; +import org.apache.iceberg.rest.requests.CommitTransactionRequest; +import org.apache.iceberg.rest.requests.CreateNamespaceRequest; +import org.apache.iceberg.rest.requests.CreateTableRequest; +import org.apache.iceberg.rest.requests.CreateViewRequest; +import org.apache.iceberg.rest.requests.RegisterTableRequest; +import org.apache.iceberg.rest.requests.RenameTableRequest; +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.LoadTableResponse; +import org.apache.iceberg.rest.responses.LoadViewResponse; +import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse; +import org.apache.iceberg.view.ViewMetadata; +import org.apache.polaris.core.admin.model.AddGrantRequest; +import org.apache.polaris.core.admin.model.Catalog; +import org.apache.polaris.core.admin.model.CatalogRole; +import org.apache.polaris.core.admin.model.CreatePrincipalRequest; +import org.apache.polaris.core.admin.model.CreatePrincipalRoleRequest; +import org.apache.polaris.core.admin.model.GrantResource; +import org.apache.polaris.core.admin.model.Principal; +import org.apache.polaris.core.admin.model.PrincipalRole; +import org.apache.polaris.core.admin.model.PrincipalWithCredentials; +import org.apache.polaris.core.admin.model.RevokeGrantRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRoleRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; +import org.apache.polaris.core.entity.PolarisPrivilege; +import org.apache.polaris.service.types.AttachPolicyRequest; +import org.apache.polaris.service.types.CommitViewRequest; +import org.apache.polaris.service.types.CreateGenericTableRequest; +import org.apache.polaris.service.types.CreatePolicyRequest; +import org.apache.polaris.service.types.DetachPolicyRequest; +import org.apache.polaris.service.types.GenericTable; +import org.apache.polaris.service.types.GetApplicablePoliciesResponse; +import org.apache.polaris.service.types.LoadPolicyResponse; +import org.apache.polaris.service.types.NotificationRequest; +import org.apache.polaris.service.types.UpdatePolicyRequest; + +/** Whitelist of types allowed for event attributes. */ +final class AllowedAttributeTypes { + private AllowedAttributeTypes() {} + + static final Set<Class<?>> ALLOWED_TYPES = + Set.of( + // Primitives + String.class, + Boolean.class, + Integer.class, + Long.class, + Double.class, Review Comment: Missing Float? Also: we could probably authorize any type that extends `Number`, including `BigInteger` and `BigDecimal`. ########## runtime/service/src/main/java/org/apache/polaris/service/events/AttributeKey.java: ########## @@ -0,0 +1,79 @@ +/* + * 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.events; + +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; + +/** + * A type-safe key for event attributes. This allows for strongly-typed attribute access while + * maintaining flexibility for custom attributes. + * + * <p>Attribute types are validated at key creation time to ensure they can be serialized. See + * {@link AllowedAttributeTypes} for the list of allowed types. + * + * @param <T> the type of the attribute value + */ +public final class AttributeKey<T> { + private final String name; + private final Class<T> type; Review Comment: I suggest that we use Guava's `com.google.common.reflect.TypeToken` here, as it handles generic types as well. ########## runtime/service/src/main/java/org/apache/polaris/service/events/PolarisEvent.java: ########## @@ -18,10 +18,66 @@ */ package org.apache.polaris.service.events; -/** Represents an event emitted by Polaris. */ -public interface PolarisEvent { +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; - PolarisEventType type(); +/** + * Represents an event emitted by Polaris. Events have a type, metadata, and a map of typed + * attributes. Use {@link #builder(PolarisEventType, PolarisEventMetadata)} to create instances. + */ +public record PolarisEvent( + PolarisEventType type, PolarisEventMetadata metadata, Map<AttributeKey<?>, Object> attributes) { + + public PolarisEvent { + attributes = Collections.unmodifiableMap(new HashMap<>(attributes)); Review Comment: ```suggestion attributes = Map.copyOf(attributes); ``` ########## runtime/service/src/main/java/org/apache/polaris/service/events/EventAttributes.java: ########## @@ -0,0 +1,250 @@ +/* + * 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.events; + +import java.util.Map; +import org.apache.iceberg.TableMetadata; +import org.apache.iceberg.catalog.Namespace; +import org.apache.iceberg.catalog.TableIdentifier; +import org.apache.iceberg.rest.requests.CommitTransactionRequest; +import org.apache.iceberg.rest.requests.CreateNamespaceRequest; +import org.apache.iceberg.rest.requests.CreateTableRequest; +import org.apache.iceberg.rest.requests.CreateViewRequest; +import org.apache.iceberg.rest.requests.RegisterTableRequest; +import org.apache.iceberg.rest.requests.RenameTableRequest; +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.LoadTableResponse; +import org.apache.iceberg.rest.responses.LoadViewResponse; +import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse; +import org.apache.iceberg.view.ViewMetadata; +import org.apache.polaris.core.admin.model.AddGrantRequest; +import org.apache.polaris.core.admin.model.Catalog; +import org.apache.polaris.core.admin.model.CatalogRole; +import org.apache.polaris.core.admin.model.CreatePrincipalRequest; +import org.apache.polaris.core.admin.model.CreatePrincipalRoleRequest; +import org.apache.polaris.core.admin.model.GrantResource; +import org.apache.polaris.core.admin.model.Principal; +import org.apache.polaris.core.admin.model.PrincipalRole; +import org.apache.polaris.core.admin.model.PrincipalWithCredentials; +import org.apache.polaris.core.admin.model.RevokeGrantRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRoleRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; +import org.apache.polaris.core.entity.PolarisPrivilege; +import org.apache.polaris.service.types.AttachPolicyRequest; +import org.apache.polaris.service.types.CommitViewRequest; +import org.apache.polaris.service.types.CreateGenericTableRequest; +import org.apache.polaris.service.types.CreatePolicyRequest; +import org.apache.polaris.service.types.DetachPolicyRequest; +import org.apache.polaris.service.types.GenericTable; +import org.apache.polaris.service.types.GetApplicablePoliciesResponse; +import org.apache.polaris.service.types.LoadPolicyResponse; +import org.apache.polaris.service.types.NotificationRequest; +import org.apache.polaris.service.types.UpdatePolicyRequest; + +/** + * Standard attribute keys for Polaris events. These keys provide type-safe access to common event + * attributes and enable automatic pruning/filtering logic. + */ +public final class EventAttributes { + private EventAttributes() {} + + // Catalog attributes + public static final AttributeKey<String> CATALOG_NAME = + AttributeKey.of("catalog_name", String.class); + public static final AttributeKey<Catalog> CATALOG = AttributeKey.of("catalog", Catalog.class); + public static final AttributeKey<UpdateCatalogRequest> UPDATE_CATALOG_REQUEST = + AttributeKey.of("update_catalog_request", UpdateCatalogRequest.class); + + // Namespace attributes + public static final AttributeKey<Namespace> NAMESPACE = + AttributeKey.of("namespace", Namespace.class); + public static final AttributeKey<String> NAMESPACE_FQN = + AttributeKey.of("namespace_fqn", String.class); + public static final AttributeKey<String> PARENT_NAMESPACE_FQN = + AttributeKey.of("parent_namespace_fqn", String.class); + public static final AttributeKey<CreateNamespaceRequest> CREATE_NAMESPACE_REQUEST = + AttributeKey.of("create_namespace_request", CreateNamespaceRequest.class); + public static final AttributeKey<UpdateNamespacePropertiesRequest> + UPDATE_NAMESPACE_PROPERTIES_REQUEST = + AttributeKey.of( + "update_namespace_properties_request", UpdateNamespacePropertiesRequest.class); + public static final AttributeKey<UpdateNamespacePropertiesResponse> + UPDATE_NAMESPACE_PROPERTIES_RESPONSE = + AttributeKey.of( + "update_namespace_properties_response", UpdateNamespacePropertiesResponse.class); + + @SuppressWarnings("unchecked") + public static final AttributeKey<Map<String, String>> NAMESPACE_PROPERTIES = + (AttributeKey<Map<String, String>>) + (AttributeKey<?>) AttributeKey.of("namespace_properties", Map.class); + + // Table attributes + public static final AttributeKey<String> TABLE_NAME = AttributeKey.of("table_name", String.class); + public static final AttributeKey<TableIdentifier> TABLE_IDENTIFIER = + AttributeKey.of("table_identifier", TableIdentifier.class); + public static final AttributeKey<CreateTableRequest> CREATE_TABLE_REQUEST = + AttributeKey.of("create_table_request", CreateTableRequest.class); + public static final AttributeKey<UpdateTableRequest> UPDATE_TABLE_REQUEST = + AttributeKey.of("update_table_request", UpdateTableRequest.class); + public static final AttributeKey<RegisterTableRequest> REGISTER_TABLE_REQUEST = + AttributeKey.of("register_table_request", RegisterTableRequest.class); + public static final AttributeKey<RenameTableRequest> RENAME_TABLE_REQUEST = + AttributeKey.of("rename_table_request", RenameTableRequest.class); + public static final AttributeKey<TableMetadata> TABLE_METADATA_BEFORE = Review Comment: My IDE is flagging some attributes as unused, which is intriguing. This is one of them. ########## runtime/service/src/main/java/org/apache/polaris/service/events/AttributeKey.java: ########## @@ -0,0 +1,79 @@ +/* + * 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.events; + +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; + +/** + * A type-safe key for event attributes. This allows for strongly-typed attribute access while + * maintaining flexibility for custom attributes. + * + * <p>Attribute types are validated at key creation time to ensure they can be serialized. See + * {@link AllowedAttributeTypes} for the list of allowed types. + * + * @param <T> the type of the attribute value + */ +public final class AttributeKey<T> { Review Comment: This class could be a record. Then the constructor could be public and we wouldn't need the static `of` method. The following should be enough (also introducing support for `TypeToken`): ```java public record AttributeKey<T>(@JsonValue String name, TypeToken<T> type) { public AttributeKey(String name, Class<T> type) { this(name, TypeToken.of(type)); } public AttributeKey(String name, TypeToken<T> type) { this.name = Objects.requireNonNull(name, "name"); this.type = Objects.requireNonNull(type, "type"); if (!AllowedAttributeTypes.isAllowed(type)) { throw new IllegalArgumentException("Type " + type + " is not allowed for event attributes"); } } } ``` ########## runtime/service/src/main/java/org/apache/polaris/service/events/PolarisEvent.java: ########## @@ -18,10 +18,66 @@ */ package org.apache.polaris.service.events; -/** Represents an event emitted by Polaris. */ -public interface PolarisEvent { +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; - PolarisEventType type(); +/** + * Represents an event emitted by Polaris. Events have a type, metadata, and a map of typed + * attributes. Use {@link #builder(PolarisEventType, PolarisEventMetadata)} to create instances. + */ +public record PolarisEvent( Review Comment: FYI we can leverage the Java Immutables library to create the builder class for us. Here is how to do it: ```java @Value.Style(depluralize = true) public record PolarisEvent( PolarisEventType type, PolarisEventMetadata metadata, Map<AttributeKey<?>, Object> attributes) { @Builder.Constructor public PolarisEvent { ... } } ``` Then we can create an event like this: ```java PolarisEvent event = new PolarisEventBuilder() .type(PolarisEventType.BEFORE_LIMIT_REQUEST_RATE) .metadata(eventMetadataFactory.create()) .putAttribute(EventAttributes.HTTP_METHOD, ctx.getMethod()) .putAttribute( EventAttributes.REQUEST_URI, ctx.getUriInfo().getAbsolutePath().toString()) .build(); ``` Another option is to transform the record into an interface and annotate with `@PolarisImmutable`. ########## runtime/service/src/main/java/org/apache/polaris/service/events/AllowedAttributeTypes.java: ########## @@ -0,0 +1,134 @@ +/* + * 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.events; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.iceberg.TableMetadata; +import org.apache.iceberg.catalog.Namespace; +import org.apache.iceberg.catalog.TableIdentifier; +import org.apache.iceberg.rest.requests.CommitTransactionRequest; +import org.apache.iceberg.rest.requests.CreateNamespaceRequest; +import org.apache.iceberg.rest.requests.CreateTableRequest; +import org.apache.iceberg.rest.requests.CreateViewRequest; +import org.apache.iceberg.rest.requests.RegisterTableRequest; +import org.apache.iceberg.rest.requests.RenameTableRequest; +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.LoadTableResponse; +import org.apache.iceberg.rest.responses.LoadViewResponse; +import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse; +import org.apache.iceberg.view.ViewMetadata; +import org.apache.polaris.core.admin.model.AddGrantRequest; +import org.apache.polaris.core.admin.model.Catalog; +import org.apache.polaris.core.admin.model.CatalogRole; +import org.apache.polaris.core.admin.model.CreatePrincipalRequest; +import org.apache.polaris.core.admin.model.CreatePrincipalRoleRequest; +import org.apache.polaris.core.admin.model.GrantResource; +import org.apache.polaris.core.admin.model.Principal; +import org.apache.polaris.core.admin.model.PrincipalRole; +import org.apache.polaris.core.admin.model.PrincipalWithCredentials; +import org.apache.polaris.core.admin.model.RevokeGrantRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRoleRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; +import org.apache.polaris.core.entity.PolarisPrivilege; +import org.apache.polaris.service.types.AttachPolicyRequest; +import org.apache.polaris.service.types.CommitViewRequest; +import org.apache.polaris.service.types.CreateGenericTableRequest; +import org.apache.polaris.service.types.CreatePolicyRequest; +import org.apache.polaris.service.types.DetachPolicyRequest; +import org.apache.polaris.service.types.GenericTable; +import org.apache.polaris.service.types.GetApplicablePoliciesResponse; +import org.apache.polaris.service.types.LoadPolicyResponse; +import org.apache.polaris.service.types.NotificationRequest; +import org.apache.polaris.service.types.UpdatePolicyRequest; + +/** Whitelist of types allowed for event attributes. */ +final class AllowedAttributeTypes { + private AllowedAttributeTypes() {} + + static final Set<Class<?>> ALLOWED_TYPES = + Set.of( + // Primitives + String.class, + Boolean.class, + Integer.class, + Long.class, + Double.class, + // Collections + List.class, + Map.class, + Set.class, + // Iceberg catalog types + Namespace.class, + TableIdentifier.class, + // Iceberg metadata types + TableMetadata.class, + ViewMetadata.class, + // Iceberg REST request types + CreateNamespaceRequest.class, Review Comment: Maybe you could check if the type implements `RESTMessage`? ########## runtime/service/src/main/java/org/apache/polaris/service/events/PolarisEvent.java: ########## @@ -18,10 +18,66 @@ */ package org.apache.polaris.service.events; -/** Represents an event emitted by Polaris. */ -public interface PolarisEvent { +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; - PolarisEventType type(); +/** + * Represents an event emitted by Polaris. Events have a type, metadata, and a map of typed + * attributes. Use {@link #builder(PolarisEventType, PolarisEventMetadata)} to create instances. + */ +public record PolarisEvent( + PolarisEventType type, PolarisEventMetadata metadata, Map<AttributeKey<?>, Object> attributes) { Review Comment: Nit: exposing a `Map<AttributeKey<?>, Object>` parameter is a bit low-level. It would be better to introduce an `AttributeMap` class with type-safe operations for get and put. That's what Netty does, cf. `io.netty.util.AttributeMap`. Very simple impl suggestion: ```java public final class AttributeMap { private final Map<AttributeKey<?>, Object> attributes = new HashMap<>(); public <T> void put(AttributeKey<T> key, T value) { attributes.put(key, value); } @SuppressWarnings("unchecked") public <T> T get(AttributeKey<T> key) { return (T) attributes.get(key); } } ``` ########## runtime/service/src/main/java/org/apache/polaris/service/events/AllowedAttributeTypes.java: ########## @@ -0,0 +1,134 @@ +/* + * 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.events; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.iceberg.TableMetadata; +import org.apache.iceberg.catalog.Namespace; +import org.apache.iceberg.catalog.TableIdentifier; +import org.apache.iceberg.rest.requests.CommitTransactionRequest; +import org.apache.iceberg.rest.requests.CreateNamespaceRequest; +import org.apache.iceberg.rest.requests.CreateTableRequest; +import org.apache.iceberg.rest.requests.CreateViewRequest; +import org.apache.iceberg.rest.requests.RegisterTableRequest; +import org.apache.iceberg.rest.requests.RenameTableRequest; +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.LoadTableResponse; +import org.apache.iceberg.rest.responses.LoadViewResponse; +import org.apache.iceberg.rest.responses.UpdateNamespacePropertiesResponse; +import org.apache.iceberg.view.ViewMetadata; +import org.apache.polaris.core.admin.model.AddGrantRequest; +import org.apache.polaris.core.admin.model.Catalog; +import org.apache.polaris.core.admin.model.CatalogRole; +import org.apache.polaris.core.admin.model.CreatePrincipalRequest; +import org.apache.polaris.core.admin.model.CreatePrincipalRoleRequest; +import org.apache.polaris.core.admin.model.GrantResource; +import org.apache.polaris.core.admin.model.Principal; +import org.apache.polaris.core.admin.model.PrincipalRole; +import org.apache.polaris.core.admin.model.PrincipalWithCredentials; +import org.apache.polaris.core.admin.model.RevokeGrantRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRequest; +import org.apache.polaris.core.admin.model.UpdateCatalogRoleRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; +import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; +import org.apache.polaris.core.entity.PolarisPrivilege; +import org.apache.polaris.service.types.AttachPolicyRequest; +import org.apache.polaris.service.types.CommitViewRequest; +import org.apache.polaris.service.types.CreateGenericTableRequest; +import org.apache.polaris.service.types.CreatePolicyRequest; +import org.apache.polaris.service.types.DetachPolicyRequest; +import org.apache.polaris.service.types.GenericTable; +import org.apache.polaris.service.types.GetApplicablePoliciesResponse; +import org.apache.polaris.service.types.LoadPolicyResponse; +import org.apache.polaris.service.types.NotificationRequest; +import org.apache.polaris.service.types.UpdatePolicyRequest; + +/** Whitelist of types allowed for event attributes. */ +final class AllowedAttributeTypes { + private AllowedAttributeTypes() {} + + static final Set<Class<?>> ALLOWED_TYPES = + Set.of( + // Primitives + String.class, + Boolean.class, + Integer.class, + Long.class, + Double.class, + // Collections + List.class, + Map.class, + Set.class, + // Iceberg catalog types + Namespace.class, + TableIdentifier.class, + // Iceberg metadata types + TableMetadata.class, + ViewMetadata.class, + // Iceberg REST request types + CreateNamespaceRequest.class, Review Comment: Here is a suggestion that leverages `TypeToken` in order to check element types of List, Set and Map: ```java static final Set<Class<?>> ALLOWED_TYPES = Set.of( String.class, Boolean.class, Number.class, RESTMessage.class, Namespace.class, TableIdentifier.class, TableMetadata.class, ViewMetadata.class, Catalog.class, Principal.class, PrincipalRole.class, PrincipalWithCredentials.class, CatalogRole.class, GrantResource.class, PolarisPrivilege.class, GenericTable.class); static boolean isAllowed(TypeToken<?> type) { Class<?> rawType = type.getRawType(); if (rawType.equals(List.class)) { TypeToken<?> elementType = type.resolveType(List.class.getTypeParameters()[0]); return isSubtypeOfAllowedType(elementType.getRawType()); } else if (rawType.equals(Set.class)) { TypeToken<?> elementType = type.resolveType(Set.class.getTypeParameters()[0]); return isSubtypeOfAllowedType(elementType.getRawType()); } else if (rawType.equals(Map.class)) { TypeToken<?> keyType = type.resolveType(Map.class.getTypeParameters()[0]); TypeToken<?> valueType = type.resolveType(Map.class.getTypeParameters()[1]); return isSubtypeOfAllowedType(keyType.getRawType()) && isSubtypeOfAllowedType(valueType.getRawType()); } else { return isSubtypeOfAllowedType(rawType); } } private static boolean isSubtypeOfAllowedType(Class<?> rawType) { return ALLOWED_TYPES.stream().anyMatch(t -> t.isAssignableFrom(rawType)); } ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
