caldav: Implement Dav Push topic

This commit is contained in:
Lennart
2025-01-12 18:45:35 +01:00
parent 8b332ade3d
commit 185eb8bddd
26 changed files with 121 additions and 76 deletions

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?, push_topic = ?\n WHERE (principal, id) = (?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 7
},
"nullable": []
},
"hash": "16a7e0cb4527060339c168ee2528416036e401f75a03100b6bfbee687b978520"
}

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "SELECT principal, id, synctoken, \"order\", displayname, description, color, timezone, timezone_id, deleted_at, subscription_url\n FROM calendars\n WHERE (principal, id) = (?, ?)", "query": "SELECT principal, id, synctoken, \"order\", displayname, description, color, timezone, timezone_id, deleted_at, subscription_url, push_topic\n FROM calendars\n WHERE (principal, id) = (?, ?)",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -57,6 +57,11 @@
"name": "subscription_url", "name": "subscription_url",
"ordinal": 10, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
},
{
"name": "push_topic",
"ordinal": 11,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -73,8 +78,9 @@
true, true,
true, true,
true, true,
true true,
false
] ]
}, },
"hash": "a74288844bf11fd318c3e688d3a4bc454d8b162d7afbeebfc448211990cd8823" "hash": "23bc337c3f74466be1d136218cf9780e4f32af800dfb75d1442f3e68dd3412de"
} }

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "INSERT INTO calendars (principal, id, displayname, description, \"order\", color, timezone, timezone_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 8
},
"nullable": []
},
"hash": "32969555ebbf5d7220c1c0d2d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6"
}

View File

@@ -1,12 +1,12 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, \"order\" = ?, color = ?, timezone = ?, timezone_id = ?\n WHERE (principal, id) = (?, ?)", "query": "UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, \"order\" = ?, color = ?, timezone = ?, timezone_id = ?, push_topic = ?\n WHERE (principal, id) = (?, ?)",
"describe": { "describe": {
"columns": [], "columns": [],
"parameters": { "parameters": {
"Right": 10 "Right": 11
}, },
"nullable": [] "nullable": []
}, },
"hash": "0d0bd23849e3e9492531fa8ce0eb22832d0c16d690cebbcb7e546cab3e942d64" "hash": "3ad4ff83b0317ee316f6e6d589d77a6f509d868de28d5b2b3724d66059223aaa"
} }

View File

@@ -1,12 +1,12 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "INSERT INTO addressbooks (principal, id, displayname, description)\n VALUES (?, ?, ?, ?)", "query": "INSERT INTO addressbooks (principal, id, displayname, description, push_topic)\n VALUES (?, ?, ?, ?, ?)",
"describe": { "describe": {
"columns": [], "columns": [],
"parameters": { "parameters": {
"Right": 4 "Right": 5
}, },
"nullable": [] "nullable": []
}, },
"hash": "51313b93f21cef5b82eb1b5a583940a6f74ba19fbf717f4d6f7351cdae4def0d" "hash": "508adcac37e0d6751924f65e9aed24c0185ce3b1bc39fd0eab7426f581aafe02"
} }

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, timezone_id, deleted_at, subscription_url\n FROM calendars\n WHERE principal = ? AND deleted_at IS NULL", "query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, timezone_id, deleted_at, subscription_url, push_topic\n FROM calendars\n WHERE principal = ? AND deleted_at IS NULL",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -57,6 +57,11 @@
"name": "subscription_url", "name": "subscription_url",
"ordinal": 10, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
},
{
"name": "push_topic",
"ordinal": 11,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -73,8 +78,9 @@
true, true,
true, true,
true, true,
true true,
false
] ]
}, },
"hash": "24ece624823b418fa96a867f57d62ed755035e88b0d9363b71aac8aedff1cbb6" "hash": "6d32ecdc8dbd73ba917f2e5f89df445ac48f7e438f1d99519dad49a127399971"
} }

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "SELECT principal, id, synctoken, displayname, description, deleted_at\n FROM addressbooks\n WHERE principal = ? AND deleted_at IS NULL", "query": "SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic\n FROM addressbooks\n WHERE principal = ? AND deleted_at IS NULL",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -32,6 +32,11 @@
"name": "deleted_at", "name": "deleted_at",
"ordinal": 5, "ordinal": 5,
"type_info": "Datetime" "type_info": "Datetime"
},
{
"name": "push_topic",
"ordinal": 6,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -43,8 +48,9 @@
false, false,
true, true,
true, true,
true true,
false
] ]
}, },
"hash": "65c6f1c6c3e7d587f3f9a126260a0e12c5d372c754ddaf0d2a1d7b65e0119799" "hash": "9be5d6df7d30a9a85aece59be810bbbb203bab874860aa05eb311259e4baaf05"
} }

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, timezone_id, deleted_at, subscription_url\n FROM calendars\n WHERE principal = ? AND deleted_at IS NOT NULL", "query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, timezone_id, deleted_at, subscription_url, push_topic\n FROM calendars\n WHERE principal = ? AND deleted_at IS NOT NULL",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -57,6 +57,11 @@
"name": "subscription_url", "name": "subscription_url",
"ordinal": 10, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
},
{
"name": "push_topic",
"ordinal": 11,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -73,8 +78,9 @@
true, true,
true, true,
true, true,
true true,
false
] ]
}, },
"hash": "9770a3a1c85aa92e7787ede67e86aa680e0831613cc9cb87cb19928fc538d35c" "hash": "ad596abc6b3826251bdf640ece558fde44fcbd4f7d27c283658de38330bef095"
} }

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO calendars (principal, id, displayname, description, \"order\", color, timezone, timezone_id, push_topic)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 9
},
"nullable": []
},
"hash": "c0d889d0738797185b10ec3e5134b804579ad4300fd8227b0d6f307281f25111"
}

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "SELECT principal, id, synctoken, displayname, description, deleted_at\n FROM addressbooks\n WHERE (principal, id) = (?, ?)", "query": "SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic\n FROM addressbooks\n WHERE (principal, id) = (?, ?)",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -32,6 +32,11 @@
"name": "deleted_at", "name": "deleted_at",
"ordinal": 5, "ordinal": 5,
"type_info": "Datetime" "type_info": "Datetime"
},
{
"name": "push_topic",
"ordinal": 6,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -43,8 +48,9 @@
false, false,
true, true,
true, true,
true true,
false
] ]
}, },
"hash": "d1dd991de4ba20fd7940ae0dbe5d6c1298ed928adbf51e2fcacd060b518786c7" "hash": "c736a652873a4795bb7a9bd96db5b41ec7e5bfba5e00aea8f7a0fa406a8d518b"
} }

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?\n WHERE (principal, id) = (?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 6
},
"nullable": []
},
"hash": "d8a41068baadd14fd412d2cbdf054f2fbb9bedea0ec2a0f06ed25d30a02338d3"
}

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "SELECT principal, id, synctoken, displayname, description, deleted_at\n FROM addressbooks\n WHERE principal = ? AND deleted_at IS NOT NULL", "query": "SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic\n FROM addressbooks\n WHERE principal = ? AND deleted_at IS NOT NULL",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -32,6 +32,11 @@
"name": "deleted_at", "name": "deleted_at",
"ordinal": 5, "ordinal": 5,
"type_info": "Datetime" "type_info": "Datetime"
},
{
"name": "push_topic",
"ordinal": 6,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -43,8 +48,9 @@
false, false,
true, true,
true, true,
true true,
false
] ]
}, },
"hash": "34e911d0f7f176cbd66ed6eb1cad7b2eb5b87acccfe9a69b1dcce05c1c303705" "hash": "e5ded4814aae1fc033bb90d27c745f76d3799958d929e8e8d16aa3ceff98e72d"
} }

3
Cargo.lock generated
View File

@@ -2631,6 +2631,7 @@ dependencies = [
"tracing", "tracing",
"tracing-actix-web", "tracing-actix-web",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@@ -2656,6 +2657,7 @@ dependencies = [
"tracing", "tracing",
"tracing-actix-web", "tracing-actix-web",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@@ -3721,6 +3723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"rand",
] ]
[[package]] [[package]]

View File

@@ -20,6 +20,7 @@ publish = false
debug = 0 debug = 0
[workspace.dependencies] [workspace.dependencies]
uuid = { version = "1.11", features = ["v4", "fast-rng"] }
async-trait = "0.1" async-trait = "0.1"
actix-web = "4.9" actix-web = "4.9"
tracing = { version = "0.1", features = ["async-await"] } tracing = { version = "0.1", features = ["async-await"] }
@@ -98,7 +99,6 @@ quote = "1.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"
heck = "0.5" heck = "0.5"
darling = "0.20" darling = "0.20"
[dependencies] [dependencies]
rustical_store = { workspace = true } rustical_store = { workspace = true }
rustical_store_sqlite = { workspace = true } rustical_store_sqlite = { workspace = true }

View File

@@ -27,3 +27,4 @@ rustical_store = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
sha2 = { workspace = true } sha2 = { workspace = true }
rustical_xml.workspace = true rustical_xml.workspace = true
uuid.workspace = true

View File

@@ -68,6 +68,7 @@ pub async fn route_mkcalendar<C: CalendarStore + ?Sized>(
deleted_at: None, deleted_at: None,
synctoken: 0, synctoken: 0,
subscription_url: None, subscription_url: None,
push_topic: uuid::Uuid::new_v4().to_string(),
}; };
match store.insert_calendar(calendar).await { match store.insert_calendar(calendar).await {

View File

@@ -17,7 +17,6 @@ use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::{Calendar, CalendarStore}; use rustical_store::{Calendar, CalendarStore};
use rustical_xml::{XmlDeserialize, XmlSerialize}; use rustical_xml::{XmlDeserialize, XmlSerialize};
use sha2::{Digest, Sha256};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames}; use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
@@ -36,9 +35,6 @@ pub enum CalendarProp {
Getcontenttype(&'static str), Getcontenttype(&'static str),
// WebDav Push // WebDav Push
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
// in DAVx5 yet
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
#[xml(skip_deserializing)] #[xml(skip_deserializing)]
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
Transports(Transports), Transports(Transports),
@@ -141,16 +137,7 @@ impl Resource for CalendarResource {
CalendarProp::Getcontenttype("text/calendar;charset=utf-8") CalendarProp::Getcontenttype("text/calendar;charset=utf-8")
} }
CalendarPropName::Transports => CalendarProp::Transports(Default::default()), CalendarPropName::Transports => CalendarProp::Transports(Default::default()),
CalendarPropName::Topic => { CalendarPropName::Topic => CalendarProp::Topic(self.cal.push_topic.to_owned()),
// TODO: Add salt since this could be public
// let url =
// CalendarResource::get_url(rmap, [&self.cal.principal, &self.cal.id]).unwrap();
let url = "TODO!".to_owned();
let mut hasher = Sha256::new();
hasher.update(url);
let topic = format!("{:x}", hasher.finalize());
CalendarProp::Topic(topic)
}
CalendarPropName::MaxResourceSize => CalendarProp::MaxResourceSize(10000000), CalendarPropName::MaxResourceSize => CalendarProp::MaxResourceSize(10000000),
CalendarPropName::SupportedReportSet => { CalendarPropName::SupportedReportSet => {
CalendarProp::SupportedReportSet(SupportedReportSet::default()) CalendarProp::SupportedReportSet(SupportedReportSet::default())

View File

@@ -26,3 +26,4 @@ rustical_dav = { workspace = true }
rustical_store = { workspace = true } rustical_store = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
rustical_xml.workspace = true rustical_xml.workspace = true
uuid.workspace = true

View File

@@ -54,6 +54,7 @@ pub async fn route_mkcol<AS: AddressbookStore + ?Sized>(
description: request.description, description: request.description,
deleted_at: None, deleted_at: None,
synctoken: 0, synctoken: 0,
push_topic: uuid::Uuid::new_v4().to_string(),
}; };
match store.get_addressbook(&principal, &addressbook_id).await { match store.get_addressbook(&principal, &addressbook_id).await {

View File

@@ -10,6 +10,7 @@ pub struct Addressbook {
pub description: Option<String>, pub description: Option<String>,
pub deleted_at: Option<NaiveDateTime>, pub deleted_at: Option<NaiveDateTime>,
pub synctoken: i64, pub synctoken: i64,
pub push_topic: String,
} }
impl Addressbook { impl Addressbook {

View File

@@ -15,6 +15,7 @@ pub struct Calendar {
pub deleted_at: Option<NaiveDateTime>, pub deleted_at: Option<NaiveDateTime>,
pub synctoken: i64, pub synctoken: i64,
pub subscription_url: Option<String>, pub subscription_url: Option<String>,
pub push_topic: String,
} }
impl Calendar { impl Calendar {

View File

@@ -5,6 +5,7 @@ use crate::{
}; };
use async_trait::async_trait; use async_trait::async_trait;
use derive_more::derive::Constructor; use derive_more::derive::Constructor;
use sha2::{Digest, Sha256};
#[derive(Constructor, Clone)] #[derive(Constructor, Clone)]
pub struct ContactBirthdayStore<AS: AddressbookStore + ?Sized>(Arc<AS>); pub struct ContactBirthdayStore<AS: AddressbookStore + ?Sized>(Arc<AS>);
@@ -24,6 +25,12 @@ fn birthday_calendar(addressbook: Addressbook) -> Calendar {
deleted_at: addressbook.deleted_at, deleted_at: addressbook.deleted_at,
synctoken: addressbook.synctoken, synctoken: addressbook.synctoken,
subscription_url: None, subscription_url: None,
push_topic: {
let mut hasher = Sha256::new();
hasher.update("birthdays");
hasher.update(addressbook.push_topic);
format!("{:x}", hasher.finalize())
},
} }
} }

View File

@@ -10,6 +10,7 @@ CREATE TABLE calendars (
timezone_id TEXT, timezone_id TEXT,
deleted_at DATETIME, deleted_at DATETIME,
subscription_url TEXT, subscription_url TEXT,
push_topic TEXT UNIQUE NOT NULL,
PRIMARY KEY (principal, id) PRIMARY KEY (principal, id)
); );

View File

@@ -5,6 +5,7 @@ CREATE TABLE addressbooks (
displayname TEXT, displayname TEXT,
description TEXT, description TEXT,
deleted_at DATETIME, deleted_at DATETIME,
push_topic TEXT UNIQUE NOT NULL,
PRIMARY KEY (principal, id) PRIMARY KEY (principal, id)
); );

View File

@@ -63,7 +63,7 @@ impl AddressbookStore for SqliteStore {
) -> Result<Addressbook, rustical_store::Error> { ) -> Result<Addressbook, rustical_store::Error> {
let addressbook = sqlx::query_as!( let addressbook = sqlx::query_as!(
Addressbook, Addressbook,
r#"SELECT principal, id, synctoken, displayname, description, deleted_at r#"SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic
FROM addressbooks FROM addressbooks
WHERE (principal, id) = (?, ?)"#, WHERE (principal, id) = (?, ?)"#,
principal, principal,
@@ -82,7 +82,7 @@ impl AddressbookStore for SqliteStore {
) -> Result<Vec<Addressbook>, rustical_store::Error> { ) -> Result<Vec<Addressbook>, rustical_store::Error> {
let addressbooks = sqlx::query_as!( let addressbooks = sqlx::query_as!(
Addressbook, Addressbook,
r#"SELECT principal, id, synctoken, displayname, description, deleted_at r#"SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic
FROM addressbooks FROM addressbooks
WHERE principal = ? AND deleted_at IS NULL"#, WHERE principal = ? AND deleted_at IS NULL"#,
principal principal
@@ -100,7 +100,7 @@ impl AddressbookStore for SqliteStore {
) -> Result<Vec<Addressbook>, rustical_store::Error> { ) -> Result<Vec<Addressbook>, rustical_store::Error> {
let addressbooks = sqlx::query_as!( let addressbooks = sqlx::query_as!(
Addressbook, Addressbook,
r#"SELECT principal, id, synctoken, displayname, description, deleted_at r#"SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic
FROM addressbooks FROM addressbooks
WHERE principal = ? AND deleted_at IS NOT NULL"#, WHERE principal = ? AND deleted_at IS NOT NULL"#,
principal principal
@@ -119,12 +119,13 @@ impl AddressbookStore for SqliteStore {
addressbook: Addressbook, addressbook: Addressbook,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
let result = sqlx::query!( let result = sqlx::query!(
r#"UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ? r#"UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?, push_topic = ?
WHERE (principal, id) = (?, ?)"#, WHERE (principal, id) = (?, ?)"#,
addressbook.principal, addressbook.principal,
addressbook.id, addressbook.id,
addressbook.displayname, addressbook.displayname,
addressbook.description, addressbook.description,
addressbook.push_topic,
principal, principal,
id id
) )
@@ -143,12 +144,13 @@ impl AddressbookStore for SqliteStore {
addressbook: Addressbook, addressbook: Addressbook,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
sqlx::query!( sqlx::query!(
r#"INSERT INTO addressbooks (principal, id, displayname, description) r#"INSERT INTO addressbooks (principal, id, displayname, description, push_topic)
VALUES (?, ?, ?, ?)"#, VALUES (?, ?, ?, ?, ?)"#,
addressbook.principal, addressbook.principal,
addressbook.id, addressbook.id,
addressbook.displayname, addressbook.displayname,
addressbook.description, addressbook.description,
addressbook.push_topic,
) )
.execute(&self.db) .execute(&self.db)
.await .await

View File

@@ -62,7 +62,7 @@ impl CalendarStore for SqliteStore {
async fn get_calendar(&self, principal: &str, id: &str) -> Result<Calendar, Error> { async fn get_calendar(&self, principal: &str, id: &str) -> Result<Calendar, Error> {
let cal = sqlx::query_as!( let cal = sqlx::query_as!(
Calendar, Calendar,
r#"SELECT principal, id, synctoken, "order", displayname, description, color, timezone, timezone_id, deleted_at, subscription_url r#"SELECT principal, id, synctoken, "order", displayname, description, color, timezone, timezone_id, deleted_at, subscription_url, push_topic
FROM calendars FROM calendars
WHERE (principal, id) = (?, ?)"#, WHERE (principal, id) = (?, ?)"#,
principal, principal,
@@ -77,7 +77,7 @@ impl CalendarStore for SqliteStore {
async fn get_calendars(&self, principal: &str) -> Result<Vec<Calendar>, Error> { async fn get_calendars(&self, principal: &str) -> Result<Vec<Calendar>, Error> {
let cals = sqlx::query_as!( let cals = sqlx::query_as!(
Calendar, Calendar,
r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, timezone_id, deleted_at, subscription_url r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, timezone_id, deleted_at, subscription_url, push_topic
FROM calendars FROM calendars
WHERE principal = ? AND deleted_at IS NULL"#, WHERE principal = ? AND deleted_at IS NULL"#,
principal principal
@@ -91,7 +91,7 @@ impl CalendarStore for SqliteStore {
async fn get_deleted_calendars(&self, principal: &str) -> Result<Vec<Calendar>, Error> { async fn get_deleted_calendars(&self, principal: &str) -> Result<Vec<Calendar>, Error> {
let cals = sqlx::query_as!( let cals = sqlx::query_as!(
Calendar, Calendar,
r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, timezone_id, deleted_at, subscription_url r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, timezone_id, deleted_at, subscription_url, push_topic
FROM calendars FROM calendars
WHERE principal = ? AND deleted_at IS NOT NULL"#, WHERE principal = ? AND deleted_at IS NOT NULL"#,
principal principal
@@ -104,8 +104,8 @@ impl CalendarStore for SqliteStore {
#[instrument] #[instrument]
async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error> { async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error> {
sqlx::query!( sqlx::query!(
r#"INSERT INTO calendars (principal, id, displayname, description, "order", color, timezone, timezone_id) r#"INSERT INTO calendars (principal, id, displayname, description, "order", color, timezone, timezone_id, push_topic)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)"#, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"#,
calendar.principal, calendar.principal,
calendar.id, calendar.id,
calendar.displayname, calendar.displayname,
@@ -113,7 +113,8 @@ impl CalendarStore for SqliteStore {
calendar.order, calendar.order,
calendar.color, calendar.color,
calendar.timezone, calendar.timezone,
calendar.timezone_id calendar.timezone_id,
calendar.push_topic,
) )
.execute(&self.db) .execute(&self.db)
.await.map_err(crate::Error::from)?; .await.map_err(crate::Error::from)?;
@@ -128,7 +129,7 @@ impl CalendarStore for SqliteStore {
calendar: Calendar, calendar: Calendar,
) -> Result<(), Error> { ) -> Result<(), Error> {
let result = sqlx::query!( let result = sqlx::query!(
r#"UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, "order" = ?, color = ?, timezone = ?, timezone_id = ? r#"UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, "order" = ?, color = ?, timezone = ?, timezone_id = ?, push_topic = ?
WHERE (principal, id) = (?, ?)"#, WHERE (principal, id) = (?, ?)"#,
calendar.principal, calendar.principal,
calendar.id, calendar.id,
@@ -138,6 +139,7 @@ impl CalendarStore for SqliteStore {
calendar.color, calendar.color,
calendar.timezone, calendar.timezone,
calendar.timezone_id, calendar.timezone_id,
calendar.push_topic,
principal, principal,
id id
).execute(&self.db).await.map_err(crate::Error::from)?; ).execute(&self.db).await.map_err(crate::Error::from)?;