addresses incoming logic
This commit is contained in:
Родитель
b054d0ece9
Коммит
02592aad3d
|
@ -61,7 +61,6 @@ CREATE TABLE IF NOT EXISTS credit_cards_data (
|
|||
cc_exp_month INTEGER,
|
||||
cc_exp_year INTEGER,
|
||||
cc_type TEXT NOT NULL,
|
||||
-- cc_exp TEXT NOT NULL, -- text format of the expiration date e.g. "[cc_exp_year]-[cc_exp_month]"
|
||||
|
||||
time_created INTEGER NOT NULL,
|
||||
time_last_used INTEGER,
|
||||
|
@ -79,7 +78,6 @@ CREATE TABLE IF NOT EXISTS credit_cards_mirror (
|
|||
cc_exp_month INTEGER,
|
||||
cc_exp_year INTEGER,
|
||||
cc_type TEXT NOT NULL,
|
||||
-- cc_exp TEXT NOT NULL, -- text format of the expiration date e.g. "[cc_exp_year]-[cc_exp_month]"
|
||||
|
||||
time_created INTEGER NOT NULL,
|
||||
time_last_used INTEGER,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
-- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
CREATE TEMP TABLE addresses_sync_staging (
|
||||
guid TEXT NOT NULL PRIMARY KEY,
|
||||
given_name TEXT NOT NULL,
|
||||
additional_name TEXT NOT NULL,
|
||||
family_name TEXT NOT NULL,
|
||||
organization TEXT NOT NULL,
|
||||
street_address TEXT NOT NULL,
|
||||
address_level3 TEXT NOT NULL,
|
||||
address_level2 TEXT NOT NULL,
|
||||
address_level1 TEXT NOT NULL,
|
||||
postal_code TEXT NOT NULL,
|
||||
country TEXT NOT NULL,
|
||||
tel TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
time_created INTEGER NOT NULL,
|
||||
time_last_used INTEGER,
|
||||
time_last_modified INTEGER NOT NULL,
|
||||
times_used INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TEMP TABLE addresses_tombstone_sync_staging (
|
||||
guid TEXT NOT NULL PRIMARY KEY
|
||||
);
|
|
@ -74,6 +74,7 @@ pub const CREDIT_CARD_COMMON_VALS: &str = "
|
|||
#[allow(dead_code)]
|
||||
const CREATE_SHARED_SCHEMA_SQL: &str = include_str!("../../sql/create_shared_schema.sql");
|
||||
const CREATE_SHARED_TRIGGERS_SQL: &str = include_str!("../../sql/create_shared_triggers.sql");
|
||||
const CREATE_SYNC_TEMP_TABLES_SQL: &str = include_str!("../../sql/create_sync_temp_tables.sql");
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn init(db: &Connection) -> Result<()> {
|
||||
|
@ -94,3 +95,9 @@ fn create(db: &Connection) -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_empty_sync_temp_tables(db: &Connection) -> Result<()> {
|
||||
log::debug!("Initializing sync temp tables");
|
||||
db.execute_batch(CREATE_SYNC_TEMP_TABLES_SQL)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
pub mod db;
|
||||
pub mod error;
|
||||
pub mod sync;
|
||||
|
||||
// Expose stuff needed by the uniffi generated code.
|
||||
use crate::db::models::address::*;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,89 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
pub mod incoming;
|
||||
|
||||
use crate::error::*;
|
||||
use rusqlite::Row;
|
||||
use serde::Serialize;
|
||||
use serde_derive::*;
|
||||
use types::Timestamp;
|
||||
|
||||
type Record = crate::sync::Record<RecordData>;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(default)]
|
||||
pub struct RecordData {
|
||||
pub given_name: String,
|
||||
|
||||
pub additional_name: String,
|
||||
|
||||
pub family_name: String,
|
||||
|
||||
pub organization: String,
|
||||
|
||||
pub street_address: String,
|
||||
|
||||
pub address_level3: String,
|
||||
|
||||
pub address_level2: String,
|
||||
|
||||
pub address_level1: String,
|
||||
|
||||
pub postal_code: String,
|
||||
|
||||
pub country: String,
|
||||
|
||||
pub tel: String,
|
||||
|
||||
pub email: String,
|
||||
|
||||
pub time_created: Timestamp,
|
||||
|
||||
pub time_last_used: Timestamp,
|
||||
|
||||
pub time_last_modified: Timestamp,
|
||||
|
||||
pub times_used: i64,
|
||||
|
||||
pub sync_change_counter: Option<i64>,
|
||||
}
|
||||
|
||||
impl RecordData {
|
||||
pub fn from_row(row: &Row<'_>, column_prefix: &str) -> Result<RecordData> {
|
||||
Ok(RecordData {
|
||||
given_name: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "given_name").as_str())?,
|
||||
additional_name: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "additional_name").as_str())?,
|
||||
family_name: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "family_name").as_str())?,
|
||||
organization: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "organization").as_str())?,
|
||||
street_address: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "street_address").as_str())?,
|
||||
address_level3: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "address_level3").as_str())?,
|
||||
address_level2: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "address_level2").as_str())?,
|
||||
address_level1: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "address_level1").as_str())?,
|
||||
postal_code: row
|
||||
.get::<_, String>(format!("{}{}", column_prefix, "postal_code").as_str())?,
|
||||
country: row.get::<_, String>(format!("{}{}", column_prefix, "country").as_str())?,
|
||||
tel: row.get::<_, String>(format!("{}{}", column_prefix, "tel").as_str())?,
|
||||
email: row.get::<_, String>(format!("{}{}", column_prefix, "email").as_str())?,
|
||||
time_created: row.get(format!("{}{}", column_prefix, "time_created").as_str())?,
|
||||
time_last_used: row.get(format!("{}{}", column_prefix, "time_last_used").as_str())?,
|
||||
time_last_modified: row
|
||||
.get(format!("{}{}", column_prefix, "time_last_modified").as_str())?,
|
||||
times_used: row.get(format!("{}{}", column_prefix, "times_used").as_str())?,
|
||||
sync_change_counter: row
|
||||
.get(format!("{}{}", column_prefix, "sync_change_counter").as_str())
|
||||
.ok(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
pub mod address;
|
||||
// pub mod credit_card;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_derive::*;
|
||||
use sync_guid::Guid as SyncGuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Record<T> {
|
||||
#[serde(rename = "id", default)]
|
||||
pub guid: SyncGuid,
|
||||
|
||||
#[serde(flatten)]
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T> Record<T> {
|
||||
fn new(guid: SyncGuid, data: T) -> Record<T> {
|
||||
Record { guid, data }
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers for tests
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use crate::db::{schema::create_empty_sync_temp_tables, test::new_mem_db, AutofillDb};
|
||||
|
||||
pub fn new_syncable_mem_db() -> AutofillDb {
|
||||
let _ = env_logger::try_init();
|
||||
let db = new_mem_db();
|
||||
create_empty_sync_temp_tables(&db).expect("should work");
|
||||
db
|
||||
}
|
||||
}
|
||||
|
||||
/// The distinct states of records to be synced which determine the `IncomingAction` to be taken.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum IncomingState<T> {
|
||||
// Only the incoming record exists. An associated local or mirror record doesn't exist.
|
||||
IncomingOnly {
|
||||
guid: SyncGuid,
|
||||
incoming: T,
|
||||
},
|
||||
// The incoming record is a tombstone.
|
||||
IncomingTombstone {
|
||||
guid: SyncGuid,
|
||||
local: Option<T>,
|
||||
has_local_changes: bool,
|
||||
has_local_tombstone: bool,
|
||||
},
|
||||
// The incoming record has an associated local record.
|
||||
HasLocal {
|
||||
guid: SyncGuid,
|
||||
incoming: T,
|
||||
merged: Record<T>,
|
||||
has_local_changes: bool,
|
||||
},
|
||||
// The incoming record doesn't have an associated local record with the same GUID.
|
||||
// A local record with the same data but a different GUID has been located.
|
||||
HasLocalDupe {
|
||||
guid: SyncGuid,
|
||||
dupe_guid: SyncGuid,
|
||||
merged: Record<T>,
|
||||
},
|
||||
// The incoming record doesn't have an associated local or local duplicate record but does
|
||||
// have a local tombstone.
|
||||
NonDeletedIncoming {
|
||||
guid: SyncGuid,
|
||||
incoming: T,
|
||||
},
|
||||
}
|
||||
|
||||
/// The distinct incoming sync actions to be preformed for incoming records.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum IncomingAction<T> {
|
||||
DeleteLocalRecord {
|
||||
guid: SyncGuid,
|
||||
},
|
||||
TakeMergedRecord {
|
||||
new_record: Record<T>,
|
||||
},
|
||||
UpdateLocalGuid {
|
||||
old_guid: SyncGuid,
|
||||
dupe_guid: SyncGuid,
|
||||
new_record: Record<T>,
|
||||
},
|
||||
TakeRemote {
|
||||
new_record: Record<T>,
|
||||
},
|
||||
DeleteLocalTombstone {
|
||||
remote_record: Record<T>,
|
||||
},
|
||||
DoNothing,
|
||||
}
|
||||
|
||||
/// Given an `IncomingState` returns the `IncomingAction` that should be performed.
|
||||
pub fn plan_incoming<T>(s: IncomingState<T>) -> IncomingAction<T> {
|
||||
match s {
|
||||
IncomingState::IncomingOnly { guid, incoming } => IncomingAction::TakeRemote {
|
||||
new_record: Record::<T>::new(guid, incoming),
|
||||
},
|
||||
IncomingState::IncomingTombstone {
|
||||
guid,
|
||||
local,
|
||||
has_local_changes,
|
||||
has_local_tombstone,
|
||||
} => match local {
|
||||
Some(_) => {
|
||||
// Note: On desktop, when there's a local record for an incoming tombstone, a local tombstone
|
||||
// would created. But we don't actually need to create a local tombstone here. If we did it would
|
||||
// immediately be deleted after being uploaded to the server.
|
||||
|
||||
if has_local_changes || has_local_tombstone {
|
||||
IncomingAction::DoNothing
|
||||
} else {
|
||||
IncomingAction::DeleteLocalRecord {
|
||||
guid: SyncGuid::new(&guid),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => IncomingAction::DoNothing,
|
||||
},
|
||||
IncomingState::HasLocal {
|
||||
guid,
|
||||
incoming,
|
||||
merged,
|
||||
has_local_changes,
|
||||
} => match has_local_changes {
|
||||
true => IncomingAction::TakeMergedRecord { new_record: merged },
|
||||
false => IncomingAction::TakeRemote {
|
||||
new_record: Record::<T>::new(guid, incoming),
|
||||
},
|
||||
},
|
||||
IncomingState::HasLocalDupe {
|
||||
guid,
|
||||
dupe_guid,
|
||||
merged,
|
||||
} => IncomingAction::UpdateLocalGuid {
|
||||
old_guid: guid,
|
||||
dupe_guid,
|
||||
new_record: merged,
|
||||
},
|
||||
IncomingState::NonDeletedIncoming { guid, incoming } => {
|
||||
IncomingAction::DeleteLocalTombstone {
|
||||
remote_record: Record::<T>::new(guid, incoming),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче