Skip to content

Commit 216205a

Browse files
NicolappsConvex, Inc.
authored and
Convex, Inc.
committed
Compare Convex schemas to a Fivetran destination (#26916)
GitOrigin-RevId: 939df3e2070e85b25de01b59aa1922f8c20af7a5
1 parent e128e37 commit 216205a

File tree

10 files changed

+1851
-55
lines changed

10 files changed

+1851
-55
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/fivetran_destination/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ tonic = { workspace = true, features = ["gzip"] }
3939
[dev-dependencies]
4040
common = { path = "../common", features = ["testing"] }
4141
convex_fivetran_common = { path = "../fivetran_common", features = ["testing"] }
42+
must-let = { workspace = true }
4243
proptest = { workspace = true }
4344

4445
[package.metadata.cargo-machete]

crates/fivetran_destination/src/api_types.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,47 @@ use chrono::{
77
DateTime,
88
Utc,
99
};
10-
use common::value::ConvexObject;
10+
use common::value::{
11+
ConvexObject,
12+
FieldPath,
13+
IdentifierFieldName,
14+
};
1115
use serde::{
1216
Deserialize,
1317
Serialize,
1418
};
1519

20+
use crate::constants::{
21+
ID_FIELD_PATH,
22+
ID_FIVETRAN_FIELD_NAME,
23+
SOFT_DELETE_FIELD_PATH,
24+
SOFT_DELETE_FIVETRAN_FIELD_NAME,
25+
SYNCED_FIELD_PATH,
26+
SYNCED_FIVETRAN_FIELD_NAME,
27+
};
28+
29+
#[derive(
30+
Hash, Eq, PartialEq, derive_more::Display, Debug, serde::Deserialize, Clone, PartialOrd, Ord,
31+
)]
32+
#[serde(transparent)]
33+
pub struct FivetranTableName(String);
34+
35+
impl FromStr for FivetranTableName {
36+
type Err = anyhow::Error;
37+
38+
fn from_str(s: &str) -> Result<Self, Self::Err> {
39+
Ok(Self(s.to_owned()))
40+
}
41+
}
42+
43+
impl Deref for FivetranTableName {
44+
type Target = str;
45+
46+
fn deref(&self) -> &str {
47+
&self.0[..]
48+
}
49+
}
50+
1651
#[derive(
1752
Hash, Eq, PartialEq, derive_more::Display, Debug, serde::Deserialize, Clone, PartialOrd, Ord,
1853
)]
@@ -35,6 +70,23 @@ impl Deref for FivetranFieldName {
3570
}
3671
}
3772

73+
impl TryInto<FieldPath> for FivetranFieldName {
74+
type Error = anyhow::Error;
75+
76+
fn try_into(self) -> Result<FieldPath, Self::Error> {
77+
Ok(if &self == SYNCED_FIVETRAN_FIELD_NAME.deref() {
78+
SYNCED_FIELD_PATH.clone()
79+
} else if &self == SOFT_DELETE_FIVETRAN_FIELD_NAME.deref() {
80+
SOFT_DELETE_FIELD_PATH.clone()
81+
} else if &self == ID_FIVETRAN_FIELD_NAME.deref() {
82+
ID_FIELD_PATH.clone()
83+
} else {
84+
let field = IdentifierFieldName::from_str(&self)?;
85+
FieldPath::for_root_field(field)
86+
})
87+
}
88+
}
89+
3890
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
3991
pub enum BatchWriteOperation {
4092
Upsert,
@@ -62,3 +114,42 @@ pub struct TruncateTableArgs {
62114
pub delete_before: Option<DateTime<Utc>>,
63115
pub delete_type: DeleteType,
64116
}
117+
118+
#[cfg(test)]
119+
mod tests {
120+
use std::str::FromStr;
121+
122+
use common::value::FieldPath;
123+
124+
use crate::api_types::FivetranFieldName;
125+
126+
#[test]
127+
fn convert_fivetran_user_fields_to_field_path() {
128+
let expected: FieldPath = FivetranFieldName::from_str("name")
129+
.unwrap()
130+
.try_into()
131+
.unwrap();
132+
assert_eq!(expected, FieldPath::from_str("name").unwrap());
133+
}
134+
135+
#[test]
136+
fn convert_fivetran_metadata_fields_to_field_path() {
137+
let expected: FieldPath = FivetranFieldName::from_str("_fivetran_synced")
138+
.unwrap()
139+
.try_into()
140+
.unwrap();
141+
assert_eq!(expected, FieldPath::from_str("fivetran.synced").unwrap());
142+
143+
let expected: FieldPath = FivetranFieldName::from_str("_fivetran_id")
144+
.unwrap()
145+
.try_into()
146+
.unwrap();
147+
assert_eq!(expected, FieldPath::from_str("fivetran.id").unwrap());
148+
149+
let expected: FieldPath = FivetranFieldName::from_str("_fivetran_deleted")
150+
.unwrap()
151+
.try_into()
152+
.unwrap();
153+
assert_eq!(expected, FieldPath::from_str("fivetran.deleted").unwrap());
154+
}
155+
}

crates/fivetran_destination/src/constants.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::sync::LazyLock;
22

33
use common::{
4+
bootstrap_model::index::database_index::IndexedFields,
5+
document::CREATION_TIME_FIELD_PATH,
46
types::IndexDescriptor,
57
value::{
68
FieldPath,
@@ -46,3 +48,30 @@ pub static SYNCED_FIELD_PATH: LazyLock<FieldPath> = LazyLock::new(|| {
4648
])
4749
.expect("Invalid field path")
4850
});
51+
52+
pub static ID_FIELD_PATH: LazyLock<FieldPath> = LazyLock::new(|| {
53+
FieldPath::new(vec![
54+
METADATA_CONVEX_FIELD_NAME.clone(),
55+
ID_CONVEX_FIELD_NAME.clone(),
56+
])
57+
.expect("Invalid field path")
58+
});
59+
60+
pub static FIVETRAN_SYNC_INDEX_WITHOUT_SOFT_DELETE_FIELDS: LazyLock<IndexedFields> =
61+
LazyLock::new(|| {
62+
IndexedFields::try_from(vec![
63+
SYNCED_FIELD_PATH.clone(),
64+
CREATION_TIME_FIELD_PATH.clone(),
65+
])
66+
.expect("Invalid IndexedFields")
67+
});
68+
69+
pub static FIVETRAN_SYNC_INDEX_WITH_SOFT_DELETE_FIELDS: LazyLock<IndexedFields> =
70+
LazyLock::new(|| {
71+
IndexedFields::try_from(vec![
72+
SOFT_DELETE_FIELD_PATH.clone(),
73+
SYNCED_FIELD_PATH.clone(),
74+
CREATION_TIME_FIELD_PATH.clone(),
75+
])
76+
.expect("Invalid IndexedFields")
77+
});

0 commit comments

Comments
 (0)