@@ -147,6 +147,7 @@ class Endpoints:
147147 create_table : str = "namespaces/{namespace}/tables"
148148 register_table : str = "namespaces/{namespace}/register"
149149 load_table : str = "namespaces/{namespace}/tables/{table}"
150+ load_credentials : str = "namespaces/{namespace}/tables/{table}/credentials"
150151 update_table : str = "namespaces/{namespace}/tables/{table}"
151152 drop_table : str = "namespaces/{namespace}/tables/{table}"
152153 table_exists : str = "namespaces/{namespace}/tables/{table}"
@@ -181,6 +182,7 @@ class Capability:
181182 V1_DELETE_TABLE = Endpoint (http_method = HttpMethod .DELETE , path = f"{ API_PREFIX } /{ Endpoints .drop_table } " )
182183 V1_RENAME_TABLE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .rename_table } " )
183184 V1_REGISTER_TABLE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .register_table } " )
185+ V1_LOAD_CREDENTIALS = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .load_credentials } " )
184186
185187 V1_LIST_VIEWS = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .list_views } " )
186188 V1_LOAD_VIEW = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .load_view } " )
@@ -293,6 +295,10 @@ class TableResponse(IcebergBaseModel):
293295 storage_credentials : list [StorageCredential ] = Field (alias = "storage-credentials" , default_factory = list )
294296
295297
298+ class LoadCredentialsResponse (IcebergBaseModel ):
299+ storage_credentials : list [StorageCredential ] = Field (alias = "storage-credentials" )
300+
301+
296302class ViewResponse (IcebergBaseModel ):
297303 metadata_location : str | None = Field (alias = "metadata-location" , default = None )
298304 metadata : ViewMetadata
@@ -545,6 +551,43 @@ def _fetch_scan_tasks(self, identifier: str | Identifier, plan_task: str) -> Sca
545551
546552 return ScanTasks .model_validate_json (response .text )
547553
554+ @retry (** _RETRY_ARGS )
555+ def _load_credentials (
556+ self ,
557+ identifier : str | Identifier ,
558+ plan_id : str | None = None ,
559+ referenced_by : str | None = None ,
560+ ) -> LoadCredentialsResponse :
561+ """Load raw vended storage credentials for a table."""
562+ self ._check_endpoint (Capability .V1_LOAD_CREDENTIALS )
563+ params : dict [str , str ] = {}
564+ if plan_id is not None :
565+ params ["planId" ] = plan_id
566+ if referenced_by is not None :
567+ params ["referenced-by" ] = referenced_by
568+
569+ response = self ._session .get (
570+ self .url (Endpoints .load_credentials , prefixed = True , ** self ._split_identifier_for_path (identifier )),
571+ params = params ,
572+ )
573+ try :
574+ response .raise_for_status ()
575+ except HTTPError as exc :
576+ _handle_non_200_response (exc , {404 : NoSuchTableError })
577+
578+ return LoadCredentialsResponse .model_validate_json (response .text )
579+
580+ def load_credentials (
581+ self ,
582+ identifier : str | Identifier ,
583+ location : str ,
584+ plan_id : str | None = None ,
585+ referenced_by : str | None = None ,
586+ ) -> Properties :
587+ """Load vended storage credentials and return the best match for a location."""
588+ credentials_response = self ._load_credentials (identifier , plan_id = plan_id , referenced_by = referenced_by )
589+ return self ._resolve_storage_credentials (credentials_response .storage_credentials , location )
590+
548591 def plan_scan (self , identifier : str | Identifier , request : PlanTableScanRequest ) -> list [FileScanTask ]:
549592 """Plan a table scan and return FileScanTasks.
550593
0 commit comments