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 pub expiration_time_ms: i64,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(rename_all = "kebab-case")]
100pub struct DeltaCredentialsResponse {
101 pub storage_credentials: Vec<DeltaStorageCredential>,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110#[serde(rename_all = "kebab-case")]
111pub struct DeltaCatalogConfig {
112 pub endpoints: Vec<String>,
114 pub protocol_version: String,
116}
117
118#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131#[serde(untagged)]
132pub enum DeltaDataType {
133 Primitive(String),
135 Array(Box<DeltaArrayType>),
136 Map(Box<DeltaMapType>),
137 Struct(Box<DeltaStructType>),
138 Decimal(DeltaDecimalType),
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
145#[serde(rename_all = "lowercase")]
146pub enum ArrayTypeTag {
147 #[default]
148 Array,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
153#[serde(rename_all = "lowercase")]
154pub enum MapTypeTag {
155 #[default]
156 Map,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
161#[serde(rename_all = "lowercase")]
162pub enum DecimalTypeTag {
163 #[default]
164 Decimal,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
169#[serde(rename_all = "kebab-case")]
170pub struct DeltaArrayType {
171 #[serde(rename = "type", default)]
172 pub type_tag: ArrayTypeTag,
173 pub element_type: DeltaDataType,
174 pub contains_null: bool,
175}
176
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179#[serde(rename_all = "kebab-case")]
180pub struct DeltaMapType {
181 #[serde(rename = "type", default)]
182 pub type_tag: MapTypeTag,
183 pub key_type: DeltaDataType,
184 pub value_type: DeltaDataType,
185 pub value_contains_null: bool,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct DeltaDecimalType {
194 #[serde(rename = "type", default)]
195 pub type_tag: DecimalTypeTag,
196 pub precision: i32,
197 pub scale: i32,
198}
199
200#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202pub struct DeltaStructField {
203 pub name: String,
204 #[serde(rename = "type")]
205 pub data_type: DeltaDataType,
206 pub nullable: bool,
207 pub metadata: BTreeMap<String, serde_json::Value>,
210}
211
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216pub struct DeltaStructType {
217 #[serde(rename = "type", default = "struct_type_tag")]
218 pub type_tag: StructTypeTag,
219 pub fields: Vec<DeltaStructField>,
220}
221
222fn struct_type_tag() -> StructTypeTag {
223 StructTypeTag::Struct
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
228#[serde(rename_all = "lowercase")]
229pub enum StructTypeTag {
230 #[default]
231 Struct,
232}
233
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
240#[serde(rename_all = "kebab-case")]
241pub struct DeltaProtocol {
242 pub min_reader_version: i32,
243 pub min_writer_version: i32,
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub reader_features: Option<Vec<String>>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub writer_features: Option<Vec<String>>,
248}
249
250#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
253#[serde(rename_all = "kebab-case")]
254pub struct DeltaSuggestedProtocol {
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub reader_features: Option<Vec<String>>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub writer_features: Option<Vec<String>>,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
267pub struct DeltaClusteringDomainMetadata {
268 #[serde(rename = "clusteringColumns")]
271 pub clustering_columns: Vec<Vec<String>>,
272}
273
274#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
276pub struct DeltaRowTrackingDomainMetadata {
277 #[serde(rename = "rowIdHighWaterMark")]
279 pub row_id_high_water_mark: i64,
280}
281
282#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
284pub struct DeltaDomainMetadataUpdates {
285 #[serde(rename = "delta.clustering", skip_serializing_if = "Option::is_none")]
286 pub delta_clustering: Option<DeltaClusteringDomainMetadata>,
287 #[serde(rename = "delta.rowTracking", skip_serializing_if = "Option::is_none")]
288 pub delta_row_tracking: Option<DeltaRowTrackingDomainMetadata>,
289}
290
291#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
297#[serde(rename_all = "kebab-case")]
298pub struct DeltaIcebergMetadata {
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub metadata_location: Option<String>,
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub converted_delta_version: Option<i64>,
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub converted_delta_timestamp: Option<i64>,
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub base_converted_delta_version: Option<i64>,
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
311pub struct DeltaUniformMetadata {
312 pub iceberg: DeltaIcebergMetadata,
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
321#[serde(rename_all = "kebab-case")]
322pub struct DeltaCommit {
323 pub version: i64,
324 pub timestamp: i64,
326 pub file_name: String,
328 pub file_size: i64,
329 pub file_modification_timestamp: i64,
331}
332
333#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
339pub struct DeltaCreateStagingTableRequest {
340 pub name: String,
341}
342
343#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
347#[serde(rename_all = "kebab-case")]
348pub struct DeltaStagingTableResponse {
349 pub table_id: String,
350 pub table_type: DeltaTableType,
351 pub location: String,
352 pub storage_credentials: Vec<DeltaStorageCredential>,
353 pub required_protocol: DeltaProtocol,
354 #[serde(skip_serializing_if = "Option::is_none")]
355 pub suggested_protocol: Option<DeltaSuggestedProtocol>,
356 pub required_properties: BTreeMap<String, Option<String>>,
358 #[serde(skip_serializing_if = "Option::is_none")]
361 pub suggested_properties: Option<BTreeMap<String, Option<String>>>,
362}
363
364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
366#[serde(rename_all = "kebab-case")]
367pub struct DeltaCreateTableRequest {
368 pub name: String,
369 pub location: String,
370 pub table_type: DeltaTableType,
371 #[serde(skip_serializing_if = "Option::is_none")]
378 pub data_source_format: Option<DeltaDataSourceFormat>,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub comment: Option<String>,
381 pub columns: DeltaStructType,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub partition_columns: Option<Vec<String>>,
384 pub protocol: DeltaProtocol,
385 pub properties: BTreeMap<String, String>,
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub domain_metadata: Option<DeltaDomainMetadataUpdates>,
388 pub last_commit_timestamp_ms: i64,
391 #[serde(skip_serializing_if = "Option::is_none")]
392 pub uniform: Option<DeltaUniformMetadata>,
393}
394
395#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
397#[serde(rename_all = "kebab-case")]
398pub struct DeltaTableMetadata {
399 pub etag: String,
401 pub table_type: DeltaTableType,
402 pub table_uuid: String,
403 pub location: String,
404 pub created_time: i64,
405 pub updated_time: i64,
406 pub columns: DeltaStructType,
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub partition_columns: Option<Vec<String>>,
409 pub properties: BTreeMap<String, String>,
410 #[serde(skip_serializing_if = "Option::is_none")]
413 pub last_commit_version: Option<i64>,
414 #[serde(skip_serializing_if = "Option::is_none")]
417 pub last_commit_timestamp_ms: Option<i64>,
418}
419
420#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
422#[serde(rename_all = "kebab-case")]
423pub struct DeltaLoadTableResponse {
424 pub metadata: DeltaTableMetadata,
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub commits: Option<Vec<DeltaCommit>>,
428 #[serde(skip_serializing_if = "Option::is_none")]
429 pub uniform: Option<DeltaUniformMetadata>,
430 #[serde(skip_serializing_if = "Option::is_none")]
433 pub latest_table_version: Option<i64>,
434}
435
436#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
438#[serde(rename_all = "kebab-case")]
439pub struct DeltaRenameTableRequest {
440 pub new_name: String,
441}
442
443#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
449#[serde(tag = "type", rename_all = "kebab-case")]
450pub enum DeltaTableRequirement {
451 AssertTableUuid { uuid: String },
453 AssertEtag { etag: String },
455}
456
457#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
460#[serde(tag = "action", rename_all = "kebab-case")]
461pub enum DeltaTableUpdate {
462 SetProperties {
463 updates: BTreeMap<String, String>,
464 },
465 RemoveProperties {
466 removals: Vec<String>,
467 },
468 SetColumns {
469 columns: DeltaStructType,
470 },
471 SetTableComment {
472 comment: String,
473 },
474 AddCommit {
475 commit: DeltaCommit,
476 #[serde(skip_serializing_if = "Option::is_none")]
477 uniform: Option<DeltaUniformMetadata>,
478 },
479 SetLatestBackfilledVersion {
480 #[serde(rename = "latest-published-version")]
481 latest_published_version: i64,
482 },
483 SetProtocol {
484 protocol: DeltaProtocol,
485 },
486 SetDomainMetadata {
487 updates: DeltaDomainMetadataUpdates,
488 },
489 RemoveDomainMetadata {
490 domains: Vec<String>,
491 },
492 SetPartitionColumns {
493 #[serde(rename = "partition-columns")]
494 partition_columns: Vec<String>,
495 },
496 UpdateMetadataSnapshotVersion {
497 #[serde(rename = "last-commit-version")]
498 last_commit_version: i64,
499 #[serde(rename = "last-commit-timestamp-ms")]
500 last_commit_timestamp_ms: i64,
501 },
502}
503
504#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
506pub struct DeltaUpdateTableRequest {
507 pub requirements: Vec<DeltaTableRequirement>,
508 pub updates: Vec<DeltaTableUpdate>,
509}
510
511#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
517#[serde(rename_all = "kebab-case")]
518pub struct DeltaFileSizeHistogram {
519 pub sorted_bin_boundaries: Vec<i64>,
520 pub file_counts: Vec<i64>,
521 pub total_bytes: Vec<i64>,
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub commit_version: Option<i64>,
524}
525
526#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
528#[serde(rename_all = "kebab-case")]
529pub struct DeltaCommitReport {
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub num_files_added: Option<i64>,
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub num_bytes_added: Option<i64>,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub num_files_removed: Option<i64>,
536 #[serde(skip_serializing_if = "Option::is_none")]
537 pub num_bytes_removed: Option<i64>,
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub num_rows_inserted: Option<i64>,
540 #[serde(skip_serializing_if = "Option::is_none")]
541 pub num_rows_removed: Option<i64>,
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub num_rows_updated: Option<i64>,
544 #[serde(skip_serializing_if = "Option::is_none")]
545 pub file_size_histogram: Option<DeltaFileSizeHistogram>,
546}
547
548#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
550#[serde(rename_all = "kebab-case")]
551pub struct DeltaReport {
552 #[serde(skip_serializing_if = "Option::is_none")]
553 pub commit_report: Option<DeltaCommitReport>,
554}
555
556#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
558#[serde(rename_all = "kebab-case")]
559pub struct DeltaReportMetricsRequest {
560 pub table_id: String,
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub report: Option<DeltaReport>,
564}
565
566#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
573pub enum DeltaErrorType {
574 BadRequestException,
575 InvalidParameterValueException,
576 UnsupportedTableFormatException,
577 NotAuthorizedException,
578 PermissionDeniedException,
579 NotFoundException,
580 NoSuchCatalogException,
581 NoSuchSchemaException,
582 NoSuchTableException,
583 AlreadyExistsException,
584 CommitVersionConflictException,
585 UpdateRequirementConflictException,
586 ResourceExhaustedException,
587 TooManyRequestsException,
588 CommitStateUnknownException,
589 InternalServerErrorException,
590 NotImplementedException,
591}
592
593impl DeltaErrorType {
594 pub fn is_not_found(&self) -> bool {
596 matches!(
597 self,
598 DeltaErrorType::NotFoundException
599 | DeltaErrorType::NoSuchCatalogException
600 | DeltaErrorType::NoSuchSchemaException
601 | DeltaErrorType::NoSuchTableException
602 )
603 }
604
605 pub fn is_already_exists(&self) -> bool {
607 matches!(self, DeltaErrorType::AlreadyExistsException)
608 }
609
610 pub fn is_commit_conflict(&self) -> bool {
613 matches!(self, DeltaErrorType::CommitVersionConflictException)
614 }
615
616 pub fn is_update_requirement_conflict(&self) -> bool {
619 matches!(self, DeltaErrorType::UpdateRequirementConflictException)
620 }
621
622 pub fn is_resource_exhausted(&self) -> bool {
626 matches!(
627 self,
628 DeltaErrorType::ResourceExhaustedException | DeltaErrorType::TooManyRequestsException
629 )
630 }
631
632 pub fn is_commit_state_unknown(&self) -> bool {
635 matches!(self, DeltaErrorType::CommitStateUnknownException)
636 }
637
638 pub fn is_unsupported_table_format(&self) -> bool {
642 matches!(self, DeltaErrorType::UnsupportedTableFormatException)
643 }
644
645 pub fn is_not_implemented(&self) -> bool {
649 matches!(self, DeltaErrorType::NotImplementedException)
650 }
651}
652
653#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct DeltaErrorModel {
656 pub message: String,
657 #[serde(rename = "type")]
658 pub error_type: DeltaErrorType,
659 pub code: u16,
661 #[serde(skip_serializing_if = "Option::is_none")]
663 pub stack: Option<Vec<String>>,
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize)]
668pub struct DeltaErrorResponse {
669 pub error: DeltaErrorModel,
670}
671
672#[cfg(test)]
673mod tests {
674 use super::*;
682 use serde_json::{Value, json};
683
684 fn round_trip<T>(value: Value)
687 where
688 T: Serialize + for<'de> Deserialize<'de>,
689 {
690 let parsed: T = serde_json::from_value(value.clone())
691 .unwrap_or_else(|e| panic!("deserialize failed: {e}\njson: {value:#}"));
692 let reserialized = serde_json::to_value(&parsed).expect("serialize");
693 assert_eq!(
694 reserialized, value,
695 "round-trip mismatch\n expected: {value:#}\n got: {reserialized:#}"
696 );
697 }
698
699 #[test]
700 fn staging_table_response() {
701 round_trip::<DeltaStagingTableResponse>(json!({
702 "table-id": "123e4567-e89b-12d3-a456-426614174000",
703 "table-type": "MANAGED",
704 "location": "s3://bucket/warehouse/catalog/schema/table",
705 "storage-credentials": [{
706 "prefix": "s3://bucket/warehouse/catalog/schema/table/",
707 "operation": "READ_WRITE",
708 "config": {
709 "s3.access-key-id": "AK...example",
710 "s3.secret-access-key": "ExampleKey",
711 "s3.session-token": "token"
712 },
713 "expiration-time-ms": 1234567890000_i64
714 }],
715 "required-protocol": {
716 "min-reader-version": 3,
717 "min-writer-version": 7,
718 "reader-features": ["deletionVectors", "vacuumProtocolCheck"],
719 "writer-features": ["catalogManaged", "deletionVectors"]
720 },
721 "suggested-protocol": {
722 "reader-features": ["typeWidening"],
723 "writer-features": ["domainMetadata", "rowTracking"]
724 },
725 "required-properties": { "delta.checkpointPolicy": "v2" },
726 "suggested-properties": {
727 "delta.rowTracking.materializedRowIdColumnName": null,
728 "delta.rowTracking.materializedRowCommitVersionColumnName": null
729 }
730 }));
731 }
732
733 #[test]
734 fn create_table_request_with_decimal_and_partition() {
735 round_trip::<DeltaCreateTableRequest>(json!({
736 "name": "sales",
737 "location": "s3://bucket/warehouse/catalog/schema/sales",
738 "table-type": "MANAGED",
739 "columns": {
740 "type": "struct",
741 "fields": [
742 { "name": "id", "type": "long", "nullable": false, "metadata": {} },
743 {
744 "name": "amount",
745 "type": { "type": "decimal", "precision": 10, "scale": 2 },
746 "nullable": true,
747 "metadata": {}
748 }
749 ]
750 },
751 "partition-columns": ["id"],
752 "protocol": {
753 "min-reader-version": 3,
754 "min-writer-version": 7,
755 "reader-features": ["deletionVectors"],
756 "writer-features": ["deletionVectors", "invariants"]
757 },
758 "properties": { "delta.enableDeletionVectors": "true" },
759 "last-commit-timestamp-ms": 1704067400000_i64
760 }));
761 }
762
763 #[test]
764 fn struct_type_carries_type_tag() {
765 round_trip::<DeltaStructType>(json!({
767 "type": "struct",
768 "fields": [
769 { "name": "id", "type": "long", "nullable": false, "metadata": {} },
770 { "name": "name", "type": "string", "nullable": true, "metadata": {} }
771 ]
772 }));
773 }
774
775 #[test]
776 fn nested_array_and_map_types() {
777 round_trip::<DeltaStructField>(json!({
778 "name": "tags",
779 "type": {
780 "type": "array",
781 "element-type": "string",
782 "contains-null": true
783 },
784 "nullable": true,
785 "metadata": {}
786 }));
787 round_trip::<DeltaStructField>(json!({
788 "name": "props",
789 "type": {
790 "type": "map",
791 "key-type": "string",
792 "value-type": "long",
793 "value-contains-null": false
794 },
795 "nullable": true,
796 "metadata": {}
797 }));
798 }
799
800 #[test]
801 fn load_table_response_with_commits() {
802 round_trip::<DeltaLoadTableResponse>(json!({
803 "metadata": {
804 "etag": "etag-1",
805 "table-type": "MANAGED",
806 "table-uuid": "123e4567-e89b-12d3-a456-426614174000",
807 "location": "s3://bucket/warehouse/catalog/schema/table",
808 "created-time": 1705600000000_i64,
809 "updated-time": 1705600000000_i64,
810 "columns": { "type": "struct", "fields": [] },
811 "properties": { "delta.checkpointPolicy": "v2" },
812 "last-commit-version": 0,
813 "last-commit-timestamp-ms": 1704067400000_i64
814 },
815 "commits": [{
816 "version": 1,
817 "timestamp": 1704067200000_i64,
818 "file-name": "00000000-0000-0000-0000-00000000002a.json",
819 "file-size": 2048,
820 "file-modification-timestamp": 1704067200000_i64
821 }],
822 "latest-table-version": 1
823 }));
824 }
825
826 #[test]
827 fn update_table_request_add_commit() {
828 round_trip::<DeltaUpdateTableRequest>(json!({
829 "requirements": [
830 { "type": "assert-table-uuid", "uuid": "123e4567-e89b-12d3-a456-426614174000" },
831 { "type": "assert-etag", "etag": "etag-1" }
832 ],
833 "updates": [
834 {
835 "action": "add-commit",
836 "commit": {
837 "version": 1,
838 "timestamp": 1704067200000_i64,
839 "file-name": "v.uuid1.json",
840 "file-size": 2048,
841 "file-modification-timestamp": 1704067200000_i64
842 }
843 },
844 { "action": "set-latest-backfilled-version", "latest-published-version": 0 }
845 ]
846 }));
847 }
848
849 #[test]
850 fn update_table_request_metadata_actions() {
851 round_trip::<DeltaUpdateTableRequest>(json!({
852 "requirements": [],
853 "updates": [
854 { "action": "set-properties", "updates": { "k": "v" } },
855 { "action": "remove-properties", "removals": ["old"] },
856 { "action": "set-table-comment", "comment": "hello" },
857 {
858 "action": "set-protocol",
859 "protocol": { "min-reader-version": 3, "min-writer-version": 7 }
860 },
861 { "action": "set-partition-columns", "partition-columns": ["id"] },
862 {
863 "action": "update-metadata-snapshot-version",
864 "last-commit-version": 5,
865 "last-commit-timestamp-ms": 1704067400000_i64
866 }
867 ]
868 }));
869 }
870
871 #[test]
872 fn update_table_domain_metadata_actions() {
873 round_trip::<DeltaUpdateTableRequest>(json!({
874 "requirements": [],
875 "updates": [
876 {
877 "action": "set-domain-metadata",
878 "updates": {
879 "delta.clustering": { "clusteringColumns": [["id"], ["address", "city"]] },
880 "delta.rowTracking": { "rowIdHighWaterMark": 42 }
881 }
882 },
883 { "action": "remove-domain-metadata", "domains": ["delta.clustering"] }
884 ]
885 }));
886 }
887
888 #[test]
889 fn credentials_response() {
890 round_trip::<DeltaCredentialsResponse>(json!({
891 "storage-credentials": [{
892 "prefix": "s3://bucket/path/",
893 "operation": "READ",
894 "config": { "s3.access-key-id": "AK", "s3.secret-access-key": "SK" },
895 "expiration-time-ms": 1234567890000_i64
896 }]
897 }));
898 }
899
900 #[test]
901 fn catalog_config() {
902 round_trip::<DeltaCatalogConfig>(json!({
903 "endpoints": ["GET /v1/temporary-path-credentials"],
904 "protocol-version": "1.0"
905 }));
906 }
907
908 #[test]
909 fn report_metrics_request() {
910 round_trip::<DeltaReportMetricsRequest>(json!({
911 "table-id": "123e4567-e89b-12d3-a456-426614174000",
912 "report": {
913 "commit-report": {
914 "num-files-added": 10,
915 "num-bytes-added": 104857600_i64,
916 "file-size-histogram": {
917 "sorted-bin-boundaries": [0, 1024, 2048],
918 "file-counts": [100, 40, 5],
919 "total-bytes": [104857600_i64, 167772160_i64, 83886080_i64],
920 "commit-version": 6
921 }
922 }
923 }
924 }));
925 }
926
927 #[test]
928 fn error_response_round_trips() {
929 round_trip::<DeltaErrorResponse>(json!({
930 "error": {
931 "message": "table not found",
932 "type": "NoSuchTableException",
933 "code": 404
934 }
935 }));
936 }
937
938 #[test]
939 fn error_response_with_stack_round_trips() {
940 round_trip::<DeltaErrorResponse>(json!({
941 "error": {
942 "message": "boom",
943 "type": "InternalServerErrorException",
944 "code": 500,
945 "stack": ["frame one", "frame two"]
946 }
947 }));
948 }
949
950 #[test]
951 fn uniform_metadata_round_trips() {
952 round_trip::<DeltaUniformMetadata>(json!({
953 "iceberg": {
954 "metadata-location": "s3://bucket/metadata/v1.json",
955 "converted-delta-version": 42,
956 "converted-delta-timestamp": 1704067200000_i64
957 }
958 }));
959 }
960}