diff --git a/Cargo.lock b/Cargo.lock index 3802cc03ff..5422c2f6a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3839,6 +3839,7 @@ dependencies = [ "aws-config", "aws-sdk-glue", "iceberg", + "iceberg-property-macro", "iceberg-storage-opendal", "iceberg_test_utils", "serde_json", diff --git a/crates/catalog/glue/Cargo.toml b/crates/catalog/glue/Cargo.toml index decb78134e..ac912bd405 100644 --- a/crates/catalog/glue/Cargo.toml +++ b/crates/catalog/glue/Cargo.toml @@ -35,6 +35,7 @@ async-trait = { workspace = true } aws-config = { workspace = true } aws-sdk-glue = { workspace = true } iceberg = { workspace = true } +iceberg-property-macro = { workspace = true } iceberg-storage-opendal = { workspace = true, features = ["opendal-s3"] } serde_json = { workspace = true } tokio = { workspace = true } diff --git a/crates/catalog/glue/src/catalog.rs b/crates/catalog/glue/src/catalog.rs index 721fbb548c..019a0197d1 100644 --- a/crates/catalog/glue/src/catalog.rs +++ b/crates/catalog/glue/src/catalog.rs @@ -36,6 +36,7 @@ use iceberg::{ Catalog, CatalogBuilder, Error, ErrorKind, MetadataLocation, Namespace, NamespaceIdent, Result, Runtime, TableCommit, TableCreation, TableIdent, }; +use iceberg_property_macro::Properties; use iceberg_storage_opendal::OpenDalStorageFactory; use crate::error::{from_aws_build_error, from_aws_sdk_error}; @@ -105,32 +106,20 @@ impl CatalogBuilder for GlueCatalogBuilder { ) -> impl Future> + Send { self.config.name = Some(name.into()); - if props.contains_key(GLUE_CATALOG_PROP_URI) { - self.config.uri = props.get(GLUE_CATALOG_PROP_URI).cloned() - } - - if props.contains_key(GLUE_CATALOG_PROP_CATALOG_ID) { - self.config.catalog_id = props.get(GLUE_CATALOG_PROP_CATALOG_ID).cloned() - } - - if props.contains_key(GLUE_CATALOG_PROP_WAREHOUSE) { - self.config.warehouse = props - .get(GLUE_CATALOG_PROP_WAREHOUSE) - .cloned() - .unwrap_or_default(); - } - - // Collect other remaining properties - self.config.props = props - .into_iter() - .filter(|(k, _)| { - k != GLUE_CATALOG_PROP_URI - && k != GLUE_CATALOG_PROP_CATALOG_ID - && k != GLUE_CATALOG_PROP_WAREHOUSE - }) - .collect(); - async move { + let mut catalog_properties = GlueCatalogProperties::from_properties(&props)?; + for property in [ + GLUE_CATALOG_PROP_URI, + GLUE_CATALOG_PROP_CATALOG_ID, + GLUE_CATALOG_PROP_WAREHOUSE, + ] { + catalog_properties.props.remove(property); + } + self.config.uri = catalog_properties.uri; + self.config.catalog_id = catalog_properties.catalog_id; + self.config.warehouse = catalog_properties.warehouse; + self.config.props = catalog_properties.props; + if self.config.name.is_none() { return Err(Error::new( ErrorKind::DataInvalid, @@ -157,6 +146,48 @@ impl CatalogBuilder for GlueCatalogBuilder { } } +#[derive(Properties)] +struct GlueCatalogProperties { + #[property(key = GLUE_CATALOG_PROP_URI, default = None)] + uri: Option, + #[property(key = GLUE_CATALOG_PROP_CATALOG_ID, default = None)] + catalog_id: Option, + #[property(key = GLUE_CATALOG_PROP_WAREHOUSE, default = "")] + warehouse: String, + #[property(prefix = "")] + props: HashMap, +} + +#[cfg(test)] +mod catalog_properties_tests { + use super::*; + + #[test] + fn test_catalog_properties() { + let properties = GlueCatalogProperties::from_properties(&HashMap::from([ + ( + GLUE_CATALOG_PROP_URI.to_string(), + "http://localhost".to_string(), + ), + ( + GLUE_CATALOG_PROP_CATALOG_ID.to_string(), + "catalog".to_string(), + ), + ( + GLUE_CATALOG_PROP_WAREHOUSE.to_string(), + "s3://warehouse".to_string(), + ), + ("aws_region".to_string(), "us-east-1".to_string()), + ])) + .unwrap(); + + assert_eq!(properties.uri.as_deref(), Some("http://localhost")); + assert_eq!(properties.catalog_id.as_deref(), Some("catalog")); + assert_eq!(properties.warehouse, "s3://warehouse"); + assert_eq!(properties.props["aws_region"], "us-east-1"); + } +} + #[derive(Debug)] /// Glue Catalog configuration pub(crate) struct GlueCatalogConfig {