1use std::collections::BTreeMap;
16
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
26pub enum DeltaTableType {
27 Managed,
28 External,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
39#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
40pub enum DeltaDataSourceFormat {
41 Delta,
42 Iceberg,
43 Hudi,
44 Parquet,
45 Csv,
46 Json,
47 Orc,
48 Avro,
49 Text,
50 UnityCatalog,
51 Deltasharing,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
57pub enum DeltaCredentialOperation {
58 Read,
59 ReadWrite,
60}
61
62#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
69pub struct DeltaStorageCredentialConfig {
70 #[serde(rename = "s3.access-key-id", skip_serializing_if = "Option::is_none")]
71 pub s3_access_key_id: Option<String>,
72 #[serde(
73 rename = "s3.secret-access-key",
74 skip_serializing_if = "Option::is_none"
75 )]
76 pub s3_secret_access_key: Option<String>,
77 #[serde(rename = "s3.session-token", skip_serializing_if = "Option::is_none")]
78 pub s3_session_token: Option<String>,
79 #[serde(rename = "azure.sas-token", skip_serializing_if = "Option::is_none")]
80 pub azure_sas_token: Option<String>,
81 #[serde(rename = "gcs.oauth-token", skip_serializing_if = "Option::is_none")]
82 pub gcs_oauth_token: Option<String>,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87#[serde(rename_all = "kebab-case")]
88pub struct DeltaStorageCredential {
89 pub prefix: String,
91 pub operation: DeltaCredentialOperation,
92 pub config: DeltaStorageCredentialConfig,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub expiration_time_ms: Option<i64>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "kebab-case")]
103pub struct DeltaCredentialsResponse {
104 pub storage_credentials: Vec<DeltaStorageCredential>,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113#[serde(rename_all = "kebab-case")]
114pub struct DeltaCatalogConfig {
115 pub endpoints: Vec<String>,
117 pub protocol_version: String,
119}
120
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134#[serde(untagged)]
135pub enum DeltaDataType {
136 Primitive(String),
138 Array(Box<DeltaArrayType>),
139 Map(Box<DeltaMapType>),
140 Struct(Box<DeltaStructType>),
141 Decimal(DeltaDecimalType),
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
148#[serde(rename_all = "lowercase")]
149pub enum ArrayTypeTag {
150 #[default]
151 Array,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
156#[serde(rename_all = "lowercase")]
157pub enum MapTypeTag {
158 #[default]
159 Map,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
164#[serde(rename_all = "lowercase")]
165pub enum DecimalTypeTag {
166 #[default]
167 Decimal,
168}
169
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172#[serde(rename_all = "kebab-case")]
173pub struct DeltaArrayType {
174 #[serde(rename = "type", default)]
175 pub type_tag: ArrayTypeTag,
176 pub element_type: DeltaDataType,
177 pub contains_null: bool,
178}
179
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182#[serde(rename_all = "kebab-case")]
183pub struct DeltaMapType {
184 #[serde(rename = "type", default)]
185 pub type_tag: MapTypeTag,
186 pub key_type: DeltaDataType,
187 pub value_type: DeltaDataType,
188 pub value_contains_null: bool,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
196pub struct DeltaDecimalType {
197 #[serde(rename = "type", default)]
198 pub type_tag: DecimalTypeTag,
199 pub precision: i32,
200 pub scale: i32,
201}
202
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct DeltaStructField {
206 pub name: String,
207 #[serde(rename = "type")]
208 pub data_type: DeltaDataType,
209 pub nullable: bool,
210 pub metadata: BTreeMap<String, serde_json::Value>,
213}
214
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
219pub struct DeltaStructType {
220 #[serde(rename = "type", default = "struct_type_tag")]
221 pub type_tag: StructTypeTag,
222 pub fields: Vec<DeltaStructField>,
223}
224
225fn struct_type_tag() -> StructTypeTag {
226 StructTypeTag::Struct
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
231#[serde(rename_all = "lowercase")]
232pub enum StructTypeTag {
233 #[default]
234 Struct,
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243#[serde(rename_all = "kebab-case")]
244pub struct DeltaProtocol {
245 pub min_reader_version: i32,
246 pub min_writer_version: i32,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub reader_features: Option<Vec<String>>,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub writer_features: Option<Vec<String>>,
251}
252
253#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
256#[serde(rename_all = "kebab-case")]
257pub struct DeltaSuggestedProtocol {
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub reader_features: Option<Vec<String>>,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 pub writer_features: Option<Vec<String>>,
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270pub struct DeltaClusteringDomainMetadata {
271 #[serde(rename = "clusteringColumns")]
274 pub clustering_columns: Vec<Vec<String>>,
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
279pub struct DeltaRowTrackingDomainMetadata {
280 #[serde(rename = "rowIdHighWaterMark")]
282 pub row_id_high_water_mark: i64,
283}
284
285#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
287pub struct DeltaDomainMetadataUpdates {
288 #[serde(rename = "delta.clustering", skip_serializing_if = "Option::is_none")]
289 pub delta_clustering: Option<DeltaClusteringDomainMetadata>,
290 #[serde(rename = "delta.rowTracking", skip_serializing_if = "Option::is_none")]
291 pub delta_row_tracking: Option<DeltaRowTrackingDomainMetadata>,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
300#[serde(rename_all = "kebab-case")]
301pub struct DeltaIcebergMetadata {
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub metadata_location: Option<String>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub converted_delta_version: Option<i64>,
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub converted_delta_timestamp: Option<i64>,
308 #[serde(skip_serializing_if = "Option::is_none")]
309 pub base_converted_delta_version: Option<i64>,
310}
311
312#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub struct DeltaUniformMetadata {
315 pub iceberg: DeltaIcebergMetadata,
316}
317
318#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
324#[serde(rename_all = "kebab-case")]
325pub struct DeltaCommit {
326 pub version: i64,
327 pub timestamp: i64,
329 pub file_name: String,
331 pub file_size: i64,
332 pub file_modification_timestamp: i64,
334}
335
336#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
342pub struct DeltaCreateStagingTableRequest {
343 pub name: String,
344}
345
346#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
350#[serde(rename_all = "kebab-case")]
351pub struct DeltaStagingTableResponse {
352 pub table_id: String,
353 pub table_type: DeltaTableType,
354 pub location: String,
355 pub storage_credentials: Vec<DeltaStorageCredential>,
356 pub required_protocol: DeltaProtocol,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub suggested_protocol: Option<DeltaSuggestedProtocol>,
359 pub required_properties: BTreeMap<String, Option<String>>,
361 #[serde(skip_serializing_if = "Option::is_none")]
364 pub suggested_properties: Option<BTreeMap<String, Option<String>>>,
365}
366
367#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
369#[serde(rename_all = "kebab-case")]
370pub struct DeltaCreateTableRequest {
371 pub name: String,
372 pub location: String,
373 pub table_type: DeltaTableType,
374 #[serde(skip_serializing_if = "Option::is_none")]
381 pub data_source_format: Option<DeltaDataSourceFormat>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub comment: Option<String>,
384 pub columns: DeltaStructType,
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub partition_columns: Option<Vec<String>>,
387 pub protocol: DeltaProtocol,
388 pub properties: BTreeMap<String, String>,
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub domain_metadata: Option<DeltaDomainMetadataUpdates>,
391 pub last_commit_timestamp_ms: i64,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub uniform: Option<DeltaUniformMetadata>,
396}
397
398#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
400#[serde(rename_all = "kebab-case")]
401pub struct DeltaTableMetadata {
402 pub etag: String,
404 pub table_type: DeltaTableType,
405 pub table_uuid: String,
406 pub location: String,
407 pub created_time: i64,
408 pub updated_time: i64,
409 pub columns: DeltaStructType,
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub partition_columns: Option<Vec<String>>,
412 pub properties: BTreeMap<String, String>,
413 #[serde(skip_serializing_if = "Option::is_none")]
416 pub last_commit_version: Option<i64>,
417 #[serde(skip_serializing_if = "Option::is_none")]
420 pub last_commit_timestamp_ms: Option<i64>,
421}
422
423#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
425#[serde(rename_all = "kebab-case")]
426pub struct DeltaLoadTableResponse {
427 pub metadata: DeltaTableMetadata,
428 #[serde(skip_serializing_if = "Option::is_none")]
430 pub commits: Option<Vec<DeltaCommit>>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub uniform: Option<DeltaUniformMetadata>,
433 #[serde(skip_serializing_if = "Option::is_none")]
436 pub latest_table_version: Option<i64>,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
441#[serde(rename_all = "kebab-case")]
442pub struct DeltaRenameTableRequest {
443 pub new_name: String,
444}
445
446#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
452#[serde(tag = "type", rename_all = "kebab-case")]
453pub enum DeltaTableRequirement {
454 AssertTableUuid { uuid: String },
456 AssertEtag { etag: String },
458}
459
460#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
463#[serde(tag = "action", rename_all = "kebab-case")]
464pub enum DeltaTableUpdate {
465 SetProperties {
466 updates: BTreeMap<String, String>,
467 },
468 RemoveProperties {
469 removals: Vec<String>,
470 },
471 SetColumns {
472 columns: DeltaStructType,
473 },
474 SetTableComment {
475 comment: String,
476 },
477 AddCommit {
478 commit: DeltaCommit,
479 #[serde(skip_serializing_if = "Option::is_none")]
480 uniform: Option<DeltaUniformMetadata>,
481 },
482 SetLatestBackfilledVersion {
483 #[serde(rename = "latest-published-version")]
484 latest_published_version: i64,
485 },
486 SetProtocol {
487 protocol: DeltaProtocol,
488 },
489 SetDomainMetadata {
490 updates: DeltaDomainMetadataUpdates,
491 },
492 RemoveDomainMetadata {
493 domains: Vec<String>,
494 },
495 SetPartitionColumns {
496 #[serde(rename = "partition-columns")]
497 partition_columns: Vec<String>,
498 },
499 UpdateMetadataSnapshotVersion {
500 #[serde(rename = "last-commit-version")]
501 last_commit_version: i64,
502 #[serde(rename = "last-commit-timestamp-ms")]
503 last_commit_timestamp_ms: i64,
504 },
505}
506
507#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
509pub struct DeltaUpdateTableRequest {
510 pub requirements: Vec<DeltaTableRequirement>,
511 pub updates: Vec<DeltaTableUpdate>,
512}
513
514#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
520#[serde(rename_all = "kebab-case")]
521pub struct DeltaFileSizeHistogram {
522 pub sorted_bin_boundaries: Vec<i64>,
523 pub file_counts: Vec<i64>,
524 pub total_bytes: Vec<i64>,
525 #[serde(skip_serializing_if = "Option::is_none")]
526 pub commit_version: Option<i64>,
527}
528
529#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
531#[serde(rename_all = "kebab-case")]
532pub struct DeltaCommitReport {
533 #[serde(skip_serializing_if = "Option::is_none")]
534 pub num_files_added: Option<i64>,
535 #[serde(skip_serializing_if = "Option::is_none")]
536 pub num_bytes_added: Option<i64>,
537 #[serde(skip_serializing_if = "Option::is_none")]
538 pub num_files_removed: Option<i64>,
539 #[serde(skip_serializing_if = "Option::is_none")]
540 pub num_bytes_removed: Option<i64>,
541 #[serde(skip_serializing_if = "Option::is_none")]
542 pub num_rows_inserted: Option<i64>,
543 #[serde(skip_serializing_if = "Option::is_none")]
544 pub num_rows_removed: Option<i64>,
545 #[serde(skip_serializing_if = "Option::is_none")]
546 pub num_rows_updated: Option<i64>,
547 #[serde(skip_serializing_if = "Option::is_none")]
548 pub file_size_histogram: Option<DeltaFileSizeHistogram>,
549}
550
551#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
553#[serde(rename_all = "kebab-case")]
554pub struct DeltaReport {
555 #[serde(skip_serializing_if = "Option::is_none")]
556 pub commit_report: Option<DeltaCommitReport>,
557}
558
559#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
561#[serde(rename_all = "kebab-case")]
562pub struct DeltaReportMetricsRequest {
563 pub table_id: String,
565 #[serde(skip_serializing_if = "Option::is_none")]
566 pub report: Option<DeltaReport>,
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
576pub enum DeltaErrorType {
577 BadRequestException,
578 InvalidParameterValueException,
579 UnsupportedTableFormatException,
580 NotAuthorizedException,
581 PermissionDeniedException,
582 NotFoundException,
583 NoSuchCatalogException,
584 NoSuchSchemaException,
585 NoSuchTableException,
586 AlreadyExistsException,
587 CommitVersionConflictException,
588 UpdateRequirementConflictException,
589 ResourceExhaustedException,
590 TooManyRequestsException,
591 CommitStateUnknownException,
592 InternalServerErrorException,
593 NotImplementedException,
594}
595
596impl DeltaErrorType {
597 pub fn is_not_found(&self) -> bool {
599 matches!(
600 self,
601 DeltaErrorType::NotFoundException
602 | DeltaErrorType::NoSuchCatalogException
603 | DeltaErrorType::NoSuchSchemaException
604 | DeltaErrorType::NoSuchTableException
605 )
606 }
607
608 pub fn is_already_exists(&self) -> bool {
610 matches!(self, DeltaErrorType::AlreadyExistsException)
611 }
612
613 pub fn is_commit_conflict(&self) -> bool {
616 matches!(self, DeltaErrorType::CommitVersionConflictException)
617 }
618
619 pub fn is_update_requirement_conflict(&self) -> bool {
622 matches!(self, DeltaErrorType::UpdateRequirementConflictException)
623 }
624
625 pub fn is_resource_exhausted(&self) -> bool {
629 matches!(
630 self,
631 DeltaErrorType::ResourceExhaustedException | DeltaErrorType::TooManyRequestsException
632 )
633 }
634
635 pub fn is_commit_state_unknown(&self) -> bool {
638 matches!(self, DeltaErrorType::CommitStateUnknownException)
639 }
640
641 pub fn is_unsupported_table_format(&self) -> bool {
645 matches!(self, DeltaErrorType::UnsupportedTableFormatException)
646 }
647
648 pub fn is_not_implemented(&self) -> bool {
652 matches!(self, DeltaErrorType::NotImplementedException)
653 }
654}
655
656#[derive(Debug, Clone, Serialize, Deserialize)]
658pub struct DeltaErrorModel {
659 pub message: String,
660 #[serde(rename = "type")]
661 pub error_type: DeltaErrorType,
662 pub code: u16,
664 #[serde(skip_serializing_if = "Option::is_none")]
666 pub stack: Option<Vec<String>>,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct DeltaErrorResponse {
672 pub error: DeltaErrorModel,
673}
674
675#[cfg(test)]
676mod tests {
677 use super::*;
685 use serde_json::{Value, json};
686
687 fn round_trip<T>(value: Value)
690 where
691 T: Serialize + for<'de> Deserialize<'de>,
692 {
693 let parsed: T = serde_json::from_value(value.clone())
694 .unwrap_or_else(|e| panic!("deserialize failed: {e}\njson: {value:#}"));
695 let reserialized = serde_json::to_value(&parsed).expect("serialize");
696 assert_eq!(
697 reserialized, value,
698 "round-trip mismatch\n expected: {value:#}\n got: {reserialized:#}"
699 );
700 }
701
702 #[test]
703 fn staging_table_response() {
704 round_trip::<DeltaStagingTableResponse>(json!({
705 "table-id": "123e4567-e89b-12d3-a456-426614174000",
706 "table-type": "MANAGED",
707 "location": "s3://bucket/warehouse/catalog/schema/table",
708 "storage-credentials": [{
709 "prefix": "s3://bucket/warehouse/catalog/schema/table/",
710 "operation": "READ_WRITE",
711 "config": {
712 "s3.access-key-id": "AK...example",
713 "s3.secret-access-key": "ExampleKey",
714 "s3.session-token": "token"
715 },
716 "expiration-time-ms": 1234567890000_i64
717 }],
718 "required-protocol": {
719 "min-reader-version": 3,
720 "min-writer-version": 7,
721 "reader-features": ["deletionVectors", "vacuumProtocolCheck"],
722 "writer-features": ["catalogManaged", "deletionVectors"]
723 },
724 "suggested-protocol": {
725 "reader-features": ["typeWidening"],
726 "writer-features": ["domainMetadata", "rowTracking"]
727 },
728 "required-properties": { "delta.checkpointPolicy": "v2" },
729 "suggested-properties": {
730 "delta.rowTracking.materializedRowIdColumnName": null,
731 "delta.rowTracking.materializedRowCommitVersionColumnName": null
732 }
733 }));
734 }
735
736 #[test]
737 fn create_table_request_with_decimal_and_partition() {
738 round_trip::<DeltaCreateTableRequest>(json!({
739 "name": "sales",
740 "location": "s3://bucket/warehouse/catalog/schema/sales",
741 "table-type": "MANAGED",
742 "columns": {
743 "type": "struct",
744 "fields": [
745 { "name": "id", "type": "long", "nullable": false, "metadata": {} },
746 {
747 "name": "amount",
748 "type": { "type": "decimal", "precision": 10, "scale": 2 },
749 "nullable": true,
750 "metadata": {}
751 }
752 ]
753 },
754 "partition-columns": ["id"],
755 "protocol": {
756 "min-reader-version": 3,
757 "min-writer-version": 7,
758 "reader-features": ["deletionVectors"],
759 "writer-features": ["deletionVectors", "invariants"]
760 },
761 "properties": { "delta.enableDeletionVectors": "true" },
762 "last-commit-timestamp-ms": 1704067400000_i64
763 }));
764 }
765
766 #[test]
767 fn struct_type_carries_type_tag() {
768 round_trip::<DeltaStructType>(json!({
770 "type": "struct",
771 "fields": [
772 { "name": "id", "type": "long", "nullable": false, "metadata": {} },
773 { "name": "name", "type": "string", "nullable": true, "metadata": {} }
774 ]
775 }));
776 }
777
778 #[test]
779 fn nested_array_and_map_types() {
780 round_trip::<DeltaStructField>(json!({
781 "name": "tags",
782 "type": {
783 "type": "array",
784 "element-type": "string",
785 "contains-null": true
786 },
787 "nullable": true,
788 "metadata": {}
789 }));
790 round_trip::<DeltaStructField>(json!({
791 "name": "props",
792 "type": {
793 "type": "map",
794 "key-type": "string",
795 "value-type": "long",
796 "value-contains-null": false
797 },
798 "nullable": true,
799 "metadata": {}
800 }));
801 }
802
803 #[test]
804 fn load_table_response_with_commits() {
805 round_trip::<DeltaLoadTableResponse>(json!({
806 "metadata": {
807 "etag": "etag-1",
808 "table-type": "MANAGED",
809 "table-uuid": "123e4567-e89b-12d3-a456-426614174000",
810 "location": "s3://bucket/warehouse/catalog/schema/table",
811 "created-time": 1705600000000_i64,
812 "updated-time": 1705600000000_i64,
813 "columns": { "type": "struct", "fields": [] },
814 "properties": { "delta.checkpointPolicy": "v2" },
815 "last-commit-version": 0,
816 "last-commit-timestamp-ms": 1704067400000_i64
817 },
818 "commits": [{
819 "version": 1,
820 "timestamp": 1704067200000_i64,
821 "file-name": "00000000-0000-0000-0000-00000000002a.json",
822 "file-size": 2048,
823 "file-modification-timestamp": 1704067200000_i64
824 }],
825 "latest-table-version": 1
826 }));
827 }
828
829 #[test]
830 fn update_table_request_add_commit() {
831 round_trip::<DeltaUpdateTableRequest>(json!({
832 "requirements": [
833 { "type": "assert-table-uuid", "uuid": "123e4567-e89b-12d3-a456-426614174000" },
834 { "type": "assert-etag", "etag": "etag-1" }
835 ],
836 "updates": [
837 {
838 "action": "add-commit",
839 "commit": {
840 "version": 1,
841 "timestamp": 1704067200000_i64,
842 "file-name": "v.uuid1.json",
843 "file-size": 2048,
844 "file-modification-timestamp": 1704067200000_i64
845 }
846 },
847 { "action": "set-latest-backfilled-version", "latest-published-version": 0 }
848 ]
849 }));
850 }
851
852 #[test]
853 fn update_table_request_metadata_actions() {
854 round_trip::<DeltaUpdateTableRequest>(json!({
855 "requirements": [],
856 "updates": [
857 { "action": "set-properties", "updates": { "k": "v" } },
858 { "action": "remove-properties", "removals": ["old"] },
859 { "action": "set-table-comment", "comment": "hello" },
860 {
861 "action": "set-protocol",
862 "protocol": { "min-reader-version": 3, "min-writer-version": 7 }
863 },
864 { "action": "set-partition-columns", "partition-columns": ["id"] },
865 {
866 "action": "update-metadata-snapshot-version",
867 "last-commit-version": 5,
868 "last-commit-timestamp-ms": 1704067400000_i64
869 }
870 ]
871 }));
872 }
873
874 #[test]
875 fn update_table_domain_metadata_actions() {
876 round_trip::<DeltaUpdateTableRequest>(json!({
877 "requirements": [],
878 "updates": [
879 {
880 "action": "set-domain-metadata",
881 "updates": {
882 "delta.clustering": { "clusteringColumns": [["id"], ["address", "city"]] },
883 "delta.rowTracking": { "rowIdHighWaterMark": 42 }
884 }
885 },
886 { "action": "remove-domain-metadata", "domains": ["delta.clustering"] }
887 ]
888 }));
889 }
890
891 #[test]
892 fn credentials_response() {
893 round_trip::<DeltaCredentialsResponse>(json!({
894 "storage-credentials": [{
895 "prefix": "s3://bucket/path/",
896 "operation": "READ",
897 "config": { "s3.access-key-id": "AK", "s3.secret-access-key": "SK" },
898 "expiration-time-ms": 1234567890000_i64
899 }]
900 }));
901 }
902
903 #[test]
904 fn catalog_config() {
905 round_trip::<DeltaCatalogConfig>(json!({
906 "endpoints": ["GET /v1/temporary-path-credentials"],
907 "protocol-version": "1.0"
908 }));
909 }
910
911 #[test]
912 fn report_metrics_request() {
913 round_trip::<DeltaReportMetricsRequest>(json!({
914 "table-id": "123e4567-e89b-12d3-a456-426614174000",
915 "report": {
916 "commit-report": {
917 "num-files-added": 10,
918 "num-bytes-added": 104857600_i64,
919 "file-size-histogram": {
920 "sorted-bin-boundaries": [0, 1024, 2048],
921 "file-counts": [100, 40, 5],
922 "total-bytes": [104857600_i64, 167772160_i64, 83886080_i64],
923 "commit-version": 6
924 }
925 }
926 }
927 }));
928 }
929
930 #[test]
931 fn error_response_round_trips() {
932 round_trip::<DeltaErrorResponse>(json!({
933 "error": {
934 "message": "table not found",
935 "type": "NoSuchTableException",
936 "code": 404
937 }
938 }));
939 }
940
941 #[test]
942 fn error_response_with_stack_round_trips() {
943 round_trip::<DeltaErrorResponse>(json!({
944 "error": {
945 "message": "boom",
946 "type": "InternalServerErrorException",
947 "code": 500,
948 "stack": ["frame one", "frame two"]
949 }
950 }));
951 }
952
953 #[test]
954 fn uniform_metadata_round_trips() {
955 round_trip::<DeltaUniformMetadata>(json!({
956 "iceberg": {
957 "metadata-location": "s3://bucket/metadata/v1.json",
958 "converted-delta-version": 42,
959 "converted-delta-timestamp": 1704067200000_i64
960 }
961 }));
962 }
963}