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)]
168#[serde(rename_all = "kebab-case")]
169pub struct DeltaArrayType {
170 #[serde(rename = "type", default)]
171 pub type_tag: ArrayTypeTag,
172 pub element_type: DeltaDataType,
173 pub contains_null: bool,
174}
175
176#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
177#[serde(rename_all = "kebab-case")]
178pub struct DeltaMapType {
179 #[serde(rename = "type", default)]
180 pub type_tag: MapTypeTag,
181 pub key_type: DeltaDataType,
182 pub value_type: DeltaDataType,
183 pub value_contains_null: bool,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
187pub struct DeltaDecimalType {
188 #[serde(rename = "type", default)]
189 pub type_tag: DecimalTypeTag,
190 pub precision: i32,
191 pub scale: i32,
192}
193
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
196pub struct DeltaStructField {
197 pub name: String,
198 #[serde(rename = "type")]
199 pub data_type: DeltaDataType,
200 pub nullable: bool,
201 pub metadata: BTreeMap<String, serde_json::Value>,
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
210pub struct DeltaStructType {
211 #[serde(rename = "type", default = "struct_type_tag")]
212 pub type_tag: StructTypeTag,
213 pub fields: Vec<DeltaStructField>,
214}
215
216fn struct_type_tag() -> StructTypeTag {
217 StructTypeTag::Struct
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
222#[serde(rename_all = "lowercase")]
223pub enum StructTypeTag {
224 #[default]
225 Struct,
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
234#[serde(rename_all = "kebab-case")]
235pub struct DeltaProtocol {
236 pub min_reader_version: i32,
237 pub min_writer_version: i32,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub reader_features: Option<Vec<String>>,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub writer_features: Option<Vec<String>>,
242}
243
244#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
247#[serde(rename_all = "kebab-case")]
248pub struct DeltaSuggestedProtocol {
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub reader_features: Option<Vec<String>>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub writer_features: Option<Vec<String>>,
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
261pub struct DeltaClusteringDomainMetadata {
262 #[serde(rename = "clusteringColumns")]
265 pub clustering_columns: Vec<Vec<String>>,
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270pub struct DeltaRowTrackingDomainMetadata {
271 #[serde(rename = "rowIdHighWaterMark")]
273 pub row_id_high_water_mark: i64,
274}
275
276#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
278pub struct DeltaDomainMetadataUpdates {
279 #[serde(rename = "delta.clustering", skip_serializing_if = "Option::is_none")]
280 pub delta_clustering: Option<DeltaClusteringDomainMetadata>,
281 #[serde(rename = "delta.rowTracking", skip_serializing_if = "Option::is_none")]
282 pub delta_row_tracking: Option<DeltaRowTrackingDomainMetadata>,
283}
284
285#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
291#[serde(rename_all = "kebab-case")]
292pub struct DeltaIcebergMetadata {
293 #[serde(skip_serializing_if = "Option::is_none")]
294 pub metadata_location: Option<String>,
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub converted_delta_version: Option<i64>,
297 #[serde(skip_serializing_if = "Option::is_none")]
298 pub converted_delta_timestamp: Option<i64>,
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub base_converted_delta_version: Option<i64>,
301}
302
303#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
305pub struct DeltaUniformMetadata {
306 pub iceberg: DeltaIcebergMetadata,
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
315#[serde(rename_all = "kebab-case")]
316pub struct DeltaCommit {
317 pub version: i64,
318 pub timestamp: i64,
320 pub file_name: String,
322 pub file_size: i64,
323 pub file_modification_timestamp: i64,
325}
326
327#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
333pub struct DeltaCreateStagingTableRequest {
334 pub name: String,
335}
336
337#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
341#[serde(rename_all = "kebab-case")]
342pub struct DeltaStagingTableResponse {
343 pub table_id: String,
344 pub table_type: DeltaTableType,
345 pub location: String,
346 pub storage_credentials: Vec<DeltaStorageCredential>,
347 pub required_protocol: DeltaProtocol,
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub suggested_protocol: Option<DeltaSuggestedProtocol>,
350 pub required_properties: BTreeMap<String, Option<String>>,
352 #[serde(skip_serializing_if = "Option::is_none")]
355 pub suggested_properties: Option<BTreeMap<String, Option<String>>>,
356}
357
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
360#[serde(rename_all = "kebab-case")]
361pub struct DeltaCreateTableRequest {
362 pub name: String,
363 pub location: String,
364 pub table_type: DeltaTableType,
365 #[serde(skip_serializing_if = "Option::is_none")]
372 pub data_source_format: Option<DeltaDataSourceFormat>,
373 #[serde(skip_serializing_if = "Option::is_none")]
374 pub comment: Option<String>,
375 pub columns: DeltaStructType,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub partition_columns: Option<Vec<String>>,
378 pub protocol: DeltaProtocol,
379 pub properties: BTreeMap<String, String>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub domain_metadata: Option<DeltaDomainMetadataUpdates>,
382 pub last_commit_timestamp_ms: i64,
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub uniform: Option<DeltaUniformMetadata>,
387}
388
389#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
391#[serde(rename_all = "kebab-case")]
392pub struct DeltaTableMetadata {
393 pub etag: String,
395 pub table_type: DeltaTableType,
396 pub table_uuid: String,
397 pub location: String,
398 pub created_time: i64,
399 pub updated_time: i64,
400 pub columns: DeltaStructType,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 pub partition_columns: Option<Vec<String>>,
403 pub properties: BTreeMap<String, String>,
404 #[serde(skip_serializing_if = "Option::is_none")]
407 pub last_commit_version: Option<i64>,
408 #[serde(skip_serializing_if = "Option::is_none")]
411 pub last_commit_timestamp_ms: Option<i64>,
412}
413
414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
416#[serde(rename_all = "kebab-case")]
417pub struct DeltaLoadTableResponse {
418 pub metadata: DeltaTableMetadata,
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub commits: Option<Vec<DeltaCommit>>,
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub uniform: Option<DeltaUniformMetadata>,
424 #[serde(skip_serializing_if = "Option::is_none")]
427 pub latest_table_version: Option<i64>,
428}
429
430#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
432#[serde(rename_all = "kebab-case")]
433pub struct DeltaRenameTableRequest {
434 pub new_name: String,
435}
436
437#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
443#[serde(tag = "type", rename_all = "kebab-case")]
444pub enum DeltaTableRequirement {
445 AssertTableUuid { uuid: String },
447 AssertEtag { etag: String },
449}
450
451#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
454#[serde(tag = "action", rename_all = "kebab-case")]
455pub enum DeltaTableUpdate {
456 SetProperties {
457 updates: BTreeMap<String, String>,
458 },
459 RemoveProperties {
460 removals: Vec<String>,
461 },
462 SetColumns {
463 columns: DeltaStructType,
464 },
465 SetTableComment {
466 comment: String,
467 },
468 AddCommit {
469 commit: DeltaCommit,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 uniform: Option<DeltaUniformMetadata>,
472 },
473 SetLatestBackfilledVersion {
474 #[serde(rename = "latest-published-version")]
475 latest_published_version: i64,
476 },
477 SetProtocol {
478 protocol: DeltaProtocol,
479 },
480 SetDomainMetadata {
481 updates: DeltaDomainMetadataUpdates,
482 },
483 RemoveDomainMetadata {
484 domains: Vec<String>,
485 },
486 SetPartitionColumns {
487 #[serde(rename = "partition-columns")]
488 partition_columns: Vec<String>,
489 },
490 UpdateMetadataSnapshotVersion {
491 #[serde(rename = "last-commit-version")]
492 last_commit_version: i64,
493 #[serde(rename = "last-commit-timestamp-ms")]
494 last_commit_timestamp_ms: i64,
495 },
496}
497
498#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
500pub struct DeltaUpdateTableRequest {
501 pub requirements: Vec<DeltaTableRequirement>,
502 pub updates: Vec<DeltaTableUpdate>,
503}
504
505#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
511#[serde(rename_all = "kebab-case")]
512pub struct DeltaFileSizeHistogram {
513 pub sorted_bin_boundaries: Vec<i64>,
514 pub file_counts: Vec<i64>,
515 pub total_bytes: Vec<i64>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 pub commit_version: Option<i64>,
518}
519
520#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
522#[serde(rename_all = "kebab-case")]
523pub struct DeltaCommitReport {
524 #[serde(skip_serializing_if = "Option::is_none")]
525 pub num_files_added: Option<i64>,
526 #[serde(skip_serializing_if = "Option::is_none")]
527 pub num_bytes_added: Option<i64>,
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub num_files_removed: Option<i64>,
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub num_bytes_removed: Option<i64>,
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub num_rows_inserted: Option<i64>,
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub num_rows_removed: Option<i64>,
536 #[serde(skip_serializing_if = "Option::is_none")]
537 pub num_rows_updated: Option<i64>,
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub file_size_histogram: Option<DeltaFileSizeHistogram>,
540}
541
542#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
544#[serde(rename_all = "kebab-case")]
545pub struct DeltaReport {
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub commit_report: Option<DeltaCommitReport>,
548}
549
550#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
552#[serde(rename_all = "kebab-case")]
553pub struct DeltaReportMetricsRequest {
554 pub table_id: String,
556 #[serde(skip_serializing_if = "Option::is_none")]
557 pub report: Option<DeltaReport>,
558}
559
560#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
567pub enum DeltaErrorType {
568 BadRequestException,
569 InvalidParameterValueException,
570 UnsupportedTableFormatException,
571 NotAuthorizedException,
572 PermissionDeniedException,
573 NotFoundException,
574 NoSuchCatalogException,
575 NoSuchSchemaException,
576 NoSuchTableException,
577 AlreadyExistsException,
578 CommitVersionConflictException,
579 UpdateRequirementConflictException,
580 ResourceExhaustedException,
581 TooManyRequestsException,
582 CommitStateUnknownException,
583 InternalServerErrorException,
584 NotImplementedException,
585}
586
587impl DeltaErrorType {
588 pub fn is_not_found(&self) -> bool {
590 matches!(
591 self,
592 DeltaErrorType::NotFoundException
593 | DeltaErrorType::NoSuchCatalogException
594 | DeltaErrorType::NoSuchSchemaException
595 | DeltaErrorType::NoSuchTableException
596 )
597 }
598
599 pub fn is_already_exists(&self) -> bool {
601 matches!(self, DeltaErrorType::AlreadyExistsException)
602 }
603
604 pub fn is_commit_conflict(&self) -> bool {
607 matches!(self, DeltaErrorType::CommitVersionConflictException)
608 }
609
610 pub fn is_update_requirement_conflict(&self) -> bool {
613 matches!(self, DeltaErrorType::UpdateRequirementConflictException)
614 }
615
616 pub fn is_resource_exhausted(&self) -> bool {
620 matches!(
621 self,
622 DeltaErrorType::ResourceExhaustedException | DeltaErrorType::TooManyRequestsException
623 )
624 }
625
626 pub fn is_commit_state_unknown(&self) -> bool {
629 matches!(self, DeltaErrorType::CommitStateUnknownException)
630 }
631
632 pub fn is_unsupported_table_format(&self) -> bool {
636 matches!(self, DeltaErrorType::UnsupportedTableFormatException)
637 }
638
639 pub fn is_not_implemented(&self) -> bool {
643 matches!(self, DeltaErrorType::NotImplementedException)
644 }
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
649pub struct DeltaErrorModel {
650 pub message: String,
651 #[serde(rename = "type")]
652 pub error_type: DeltaErrorType,
653 pub code: u16,
655 #[serde(skip_serializing_if = "Option::is_none")]
657 pub stack: Option<Vec<String>>,
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct DeltaErrorResponse {
663 pub error: DeltaErrorModel,
664}
665
666#[cfg(test)]
667mod tests {
668 use super::*;
676 use serde_json::{Value, json};
677
678 fn round_trip<T>(value: Value)
681 where
682 T: Serialize + for<'de> Deserialize<'de>,
683 {
684 let parsed: T = serde_json::from_value(value.clone())
685 .unwrap_or_else(|e| panic!("deserialize failed: {e}\njson: {value:#}"));
686 let reserialized = serde_json::to_value(&parsed).expect("serialize");
687 assert_eq!(
688 reserialized, value,
689 "round-trip mismatch\n expected: {value:#}\n got: {reserialized:#}"
690 );
691 }
692
693 #[test]
694 fn staging_table_response() {
695 round_trip::<DeltaStagingTableResponse>(json!({
696 "table-id": "123e4567-e89b-12d3-a456-426614174000",
697 "table-type": "MANAGED",
698 "location": "s3://bucket/warehouse/catalog/schema/table",
699 "storage-credentials": [{
700 "prefix": "s3://bucket/warehouse/catalog/schema/table/",
701 "operation": "READ_WRITE",
702 "config": {
703 "s3.access-key-id": "AK...example",
704 "s3.secret-access-key": "ExampleKey",
705 "s3.session-token": "token"
706 },
707 "expiration-time-ms": 1234567890000_i64
708 }],
709 "required-protocol": {
710 "min-reader-version": 3,
711 "min-writer-version": 7,
712 "reader-features": ["deletionVectors", "vacuumProtocolCheck"],
713 "writer-features": ["catalogManaged", "deletionVectors"]
714 },
715 "suggested-protocol": {
716 "reader-features": ["typeWidening"],
717 "writer-features": ["domainMetadata", "rowTracking"]
718 },
719 "required-properties": { "delta.checkpointPolicy": "v2" },
720 "suggested-properties": {
721 "delta.rowTracking.materializedRowIdColumnName": null,
722 "delta.rowTracking.materializedRowCommitVersionColumnName": null
723 }
724 }));
725 }
726
727 #[test]
728 fn create_table_request_with_decimal_and_partition() {
729 round_trip::<DeltaCreateTableRequest>(json!({
730 "name": "sales",
731 "location": "s3://bucket/warehouse/catalog/schema/sales",
732 "table-type": "MANAGED",
733 "columns": {
734 "type": "struct",
735 "fields": [
736 { "name": "id", "type": "long", "nullable": false, "metadata": {} },
737 {
738 "name": "amount",
739 "type": { "type": "decimal", "precision": 10, "scale": 2 },
740 "nullable": true,
741 "metadata": {}
742 }
743 ]
744 },
745 "partition-columns": ["id"],
746 "protocol": {
747 "min-reader-version": 3,
748 "min-writer-version": 7,
749 "reader-features": ["deletionVectors"],
750 "writer-features": ["deletionVectors", "invariants"]
751 },
752 "properties": { "delta.enableDeletionVectors": "true" },
753 "last-commit-timestamp-ms": 1704067400000_i64
754 }));
755 }
756
757 #[test]
758 fn struct_type_carries_type_tag() {
759 round_trip::<DeltaStructType>(json!({
761 "type": "struct",
762 "fields": [
763 { "name": "id", "type": "long", "nullable": false, "metadata": {} },
764 { "name": "name", "type": "string", "nullable": true, "metadata": {} }
765 ]
766 }));
767 }
768
769 #[test]
770 fn nested_array_and_map_types() {
771 round_trip::<DeltaStructField>(json!({
772 "name": "tags",
773 "type": {
774 "type": "array",
775 "element-type": "string",
776 "contains-null": true
777 },
778 "nullable": true,
779 "metadata": {}
780 }));
781 round_trip::<DeltaStructField>(json!({
782 "name": "props",
783 "type": {
784 "type": "map",
785 "key-type": "string",
786 "value-type": "long",
787 "value-contains-null": false
788 },
789 "nullable": true,
790 "metadata": {}
791 }));
792 }
793
794 #[test]
795 fn load_table_response_with_commits() {
796 round_trip::<DeltaLoadTableResponse>(json!({
797 "metadata": {
798 "etag": "etag-1",
799 "table-type": "MANAGED",
800 "table-uuid": "123e4567-e89b-12d3-a456-426614174000",
801 "location": "s3://bucket/warehouse/catalog/schema/table",
802 "created-time": 1705600000000_i64,
803 "updated-time": 1705600000000_i64,
804 "columns": { "type": "struct", "fields": [] },
805 "properties": { "delta.checkpointPolicy": "v2" },
806 "last-commit-version": 0,
807 "last-commit-timestamp-ms": 1704067400000_i64
808 },
809 "commits": [{
810 "version": 1,
811 "timestamp": 1704067200000_i64,
812 "file-name": "00000000-0000-0000-0000-00000000002a.json",
813 "file-size": 2048,
814 "file-modification-timestamp": 1704067200000_i64
815 }],
816 "latest-table-version": 1
817 }));
818 }
819
820 #[test]
821 fn update_table_request_add_commit() {
822 round_trip::<DeltaUpdateTableRequest>(json!({
823 "requirements": [
824 { "type": "assert-table-uuid", "uuid": "123e4567-e89b-12d3-a456-426614174000" },
825 { "type": "assert-etag", "etag": "etag-1" }
826 ],
827 "updates": [
828 {
829 "action": "add-commit",
830 "commit": {
831 "version": 1,
832 "timestamp": 1704067200000_i64,
833 "file-name": "v.uuid1.json",
834 "file-size": 2048,
835 "file-modification-timestamp": 1704067200000_i64
836 }
837 },
838 { "action": "set-latest-backfilled-version", "latest-published-version": 0 }
839 ]
840 }));
841 }
842
843 #[test]
844 fn update_table_request_metadata_actions() {
845 round_trip::<DeltaUpdateTableRequest>(json!({
846 "requirements": [],
847 "updates": [
848 { "action": "set-properties", "updates": { "k": "v" } },
849 { "action": "remove-properties", "removals": ["old"] },
850 { "action": "set-table-comment", "comment": "hello" },
851 {
852 "action": "set-protocol",
853 "protocol": { "min-reader-version": 3, "min-writer-version": 7 }
854 },
855 { "action": "set-partition-columns", "partition-columns": ["id"] },
856 {
857 "action": "update-metadata-snapshot-version",
858 "last-commit-version": 5,
859 "last-commit-timestamp-ms": 1704067400000_i64
860 }
861 ]
862 }));
863 }
864
865 #[test]
866 fn update_table_domain_metadata_actions() {
867 round_trip::<DeltaUpdateTableRequest>(json!({
868 "requirements": [],
869 "updates": [
870 {
871 "action": "set-domain-metadata",
872 "updates": {
873 "delta.clustering": { "clusteringColumns": [["id"], ["address", "city"]] },
874 "delta.rowTracking": { "rowIdHighWaterMark": 42 }
875 }
876 },
877 { "action": "remove-domain-metadata", "domains": ["delta.clustering"] }
878 ]
879 }));
880 }
881
882 #[test]
883 fn credentials_response() {
884 round_trip::<DeltaCredentialsResponse>(json!({
885 "storage-credentials": [{
886 "prefix": "s3://bucket/path/",
887 "operation": "READ",
888 "config": { "s3.access-key-id": "AK", "s3.secret-access-key": "SK" },
889 "expiration-time-ms": 1234567890000_i64
890 }]
891 }));
892 }
893
894 #[test]
895 fn catalog_config() {
896 round_trip::<DeltaCatalogConfig>(json!({
897 "endpoints": ["GET /v1/config"],
898 "protocol-version": "1.0"
899 }));
900 }
901
902 #[test]
903 fn report_metrics_request() {
904 round_trip::<DeltaReportMetricsRequest>(json!({
905 "table-id": "123e4567-e89b-12d3-a456-426614174000",
906 "report": {
907 "commit-report": {
908 "num-files-added": 10,
909 "num-bytes-added": 104857600_i64,
910 "file-size-histogram": {
911 "sorted-bin-boundaries": [0, 1024, 2048],
912 "file-counts": [100, 40, 5],
913 "total-bytes": [104857600_i64, 167772160_i64, 83886080_i64],
914 "commit-version": 6
915 }
916 }
917 }
918 }));
919 }
920
921 #[test]
922 fn error_response_round_trips() {
923 round_trip::<DeltaErrorResponse>(json!({
924 "error": {
925 "message": "table not found",
926 "type": "NoSuchTableException",
927 "code": 404
928 }
929 }));
930 }
931
932 #[test]
933 fn error_response_with_stack_round_trips() {
934 round_trip::<DeltaErrorResponse>(json!({
935 "error": {
936 "message": "boom",
937 "type": "InternalServerErrorException",
938 "code": 500,
939 "stack": ["frame one", "frame two"]
940 }
941 }));
942 }
943
944 #[test]
945 fn uniform_metadata_round_trips() {
946 round_trip::<DeltaUniformMetadata>(json!({
947 "iceberg": {
948 "metadata-location": "s3://bucket/metadata/v1.json",
949 "converted-delta-version": 42,
950 "converted-delta-timestamp": 1704067200000_i64
951 }
952 }));
953 }
954}