Fix feed entry id issues

This commit is contained in:
Greg Shuflin 2025-02-05 02:31:29 -08:00
parent 44208470f8
commit c89ee29d4d
10 changed files with 58 additions and 52 deletions

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE feed_entries SET marked_read = NULL WHERE local_id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "241fab33b2e6ffcba26cb3bd668f2f601d0ee2f24f5e8612323b66e5a2d2cbe9"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "\n SELECT \n id as \"id!: String\",\n entry_id,\n title,\n published as \"published: Option<chrono::DateTime<Utc>>\",\n updated as \"updated: Option<chrono::DateTime<Utc>>\",\n summary,\n content,\n link,\n marked_read as \"marked_read: Option<chrono::DateTime<Utc>>\"\n FROM feed_entries \n WHERE feed_id = ?\n ORDER BY published DESC NULLS LAST\n LIMIT ?\n ",
"query": "\n SELECT \n id as \"id!: String\",\n local_id as \"local_id!: String\",\n title,\n published as \"published: Option<chrono::DateTime<Utc>>\",\n updated as \"updated: Option<chrono::DateTime<Utc>>\",\n summary,\n content,\n link,\n marked_read as \"marked_read: Option<chrono::DateTime<Utc>>\"\n FROM feed_entries \n WHERE feed_id = ?\n ORDER BY published DESC NULLS LAST\n LIMIT ?\n ",
"describe": {
"columns": [
{
@ -9,7 +9,7 @@
"type_info": "Text"
},
{
"name": "entry_id",
"name": "local_id!: String",
"ordinal": 1,
"type_info": "Text"
},
@ -64,5 +64,5 @@
true
]
},
"hash": "7698fc853218a67cf22338697be3b504220e8fbf845e32945273c7e5cd579152"
"hash": "2bbed6f20243ced25ac9359afefafb5ddffdff949250e0e4e35bf399fc0199fc"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "\n SELECT 1 as found FROM feed_entries e\n JOIN feeds f ON e.feed_id = f.feed_id\n WHERE e.id = ? AND f.user_id = ?\n ",
"query": "\n SELECT 1 as found FROM feed_entries e\n JOIN feeds f ON e.feed_id = f.feed_id\n WHERE e.local_id = ? AND f.user_id = ?\n ",
"describe": {
"columns": [
{
@ -16,5 +16,5 @@
false
]
},
"hash": "ce12e0b02596a4045fb2cd91d566aa633109a65afcecc92564a785282e67e193"
"hash": "3744bfed27760e0d6029063116352c42d90a3e4a5ea924e241437ce312535cc1"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO feed_entries (\n id, feed_id, entry_id, title, published, updated, summary, content, link, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (feed_id, id) DO UPDATE SET\n title = excluded.title,\n published = excluded.published,\n updated = excluded.updated,\n summary = excluded.summary,\n content = excluded.content,\n link = excluded.link\n ",
"query": "\n INSERT INTO feed_entries (\n id, feed_id, local_id, title, published, updated, summary, content, link, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (feed_id, id) DO UPDATE SET\n title = excluded.title,\n published = excluded.published,\n updated = excluded.updated,\n summary = excluded.summary,\n content = excluded.content,\n link = excluded.link\n ",
"describe": {
"columns": [],
"parameters": {
@ -8,5 +8,5 @@
},
"nullable": []
},
"hash": "3c8d358e534f35c6e59b1d94cf28175b5d9b60a662388dec1c20a79e298cac4f"
"hash": "6efd0a1292d597cce5ea84e4fada30035dd40d557f5c305ec7b5bb6b0788948c"
}

View File

@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE feed_entries SET marked_read = ? WHERE local_id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "7cb047b30c2454689b447f4f2717b665d0094b645205e20390c6dec72fd15910"
}

View File

@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE feed_entries SET marked_read = ? WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "ad2e09903009082ca746a9dc75cb04831b016d17cc7ce32fa86681431071fef6"
}

View File

@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE feed_entries SET marked_read = NULL WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "c64e0927e594985ee8d2be7190c8f76fea57c4c815981673c92e709b17b9204b"
}

View File

@ -0,0 +1,2 @@
-- Rename entry_id column to local_id
ALTER TABLE feed_entries RENAME COLUMN entry_id TO local_id;

View File

@ -23,8 +23,11 @@ pub struct FeedPollResponse {
#[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")]
struct Entry {
id: Uuid,
entry_id: String,
/// id is the id from the feed, and is the primary key of entries
id: String,
/// local_id is a UUID generated locally. it is used because we don't have control over the
/// exact format of the id from the feed entry
local_id: Uuid,
title: String,
published: Option<DateTime<Utc>>,
updated: Option<DateTime<Utc>>,
@ -74,11 +77,12 @@ async fn update_entry_db(
let published = entry.published.map(|dt| dt.to_rfc3339());
let updated = entry.updated.map(|dt| dt.to_rfc3339());
let entry_id = entry.id.to_string();
let local_id = entry.local_id.to_string();
let result = sqlx::query!(
r#"
INSERT INTO feed_entries (
id, feed_id, entry_id, title, published, updated, summary, content, link, created_at
id, feed_id, local_id, title, published, updated, summary, content, link, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (feed_id, id) DO UPDATE SET
title = excluded.title,
@ -88,9 +92,9 @@ async fn update_entry_db(
content = excluded.content,
link = excluded.link
"#,
entry_id,
entry.id,
feed_id,
entry.entry_id,
local_id,
entry.title,
published,
updated,
@ -126,7 +130,7 @@ async fn read_entries(feed_id: &str, db: &mut SqliteConnection) -> Result<Vec<En
r#"
SELECT
id as "id!: String",
entry_id,
local_id as "local_id!: String",
title,
published as "published: Option<chrono::DateTime<Utc>>",
updated as "updated: Option<chrono::DateTime<Utc>>",
@ -158,8 +162,8 @@ async fn read_entries(feed_id: &str, db: &mut SqliteConnection) -> Result<Vec<En
.and_then(|s| serde::json::from_str(s).ok());
Ok(Entry {
id: Uuid::parse_str(&row.id).map_err(|_| Status::InternalServerError)?,
entry_id: row.entry_id.clone(),
id: row.id.clone(),
local_id: Uuid::parse_str(&row.local_id).map_err(|_| Status::InternalServerError)?,
title: row.title.clone(),
published: row.published.flatten(),
updated: row.updated.flatten(),
@ -187,8 +191,8 @@ async fn fetch_new_entries(url: &Url) -> Result<Vec<Entry>, Status> {
.entries
.into_iter()
.map(|feed_entry| Entry {
id: Uuid::new_v4(),
entry_id: feed_entry.id,
id: feed_entry.id,
local_id: Uuid::new_v4(),
title: get(feed_entry.title, "title"),
published: feed_entry.published,
updated: feed_entry.updated,
@ -242,10 +246,10 @@ pub async fn poll_feed(
Ok(Json(FeedPollResponse { count, entries }))
}
#[patch("/entries/<entry_id>/state", data = "<state>")]
#[patch("/entries/<local_id>/state", data = "<state>")]
pub async fn update_entry_state(
mut db: Connection<Db>,
entry_id: &str,
local_id: &str,
user: AuthenticatedUser,
state: Json<EntryStateUpdate>,
) -> Result<Status, Status> {
@ -257,9 +261,9 @@ pub async fn update_entry_state(
r#"
SELECT 1 as found FROM feed_entries e
JOIN feeds f ON e.feed_id = f.feed_id
WHERE e.id = ? AND f.user_id = ?
WHERE e.local_id = ? AND f.user_id = ?
"#,
entry_id,
local_id,
user_id,
)
.fetch_optional(&mut **db)
@ -278,16 +282,16 @@ pub async fn update_entry_state(
let now = Utc::now();
let result = if read {
sqlx::query!(
"UPDATE feed_entries SET marked_read = ? WHERE id = ?",
"UPDATE feed_entries SET marked_read = ? WHERE local_id = ?",
now,
entry_id
local_id
)
.execute(&mut **db)
.await
} else {
sqlx::query!(
"UPDATE feed_entries SET marked_read = NULL WHERE id = ?",
entry_id
"UPDATE feed_entries SET marked_read = NULL WHERE local_id = ?",
local_id
)
.execute(&mut **db)
.await

View File

@ -57,7 +57,7 @@ function renderFeedEntries(entries) {
: '<i class="fa-regular fa-square"></i>';
readToggle.onclick = async () => {
try {
const response = await fetch(`/entries/${entry.id}/state`, {
const response = await fetch(`/entries/${entry.local_id}/state`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
@ -90,7 +90,7 @@ function renderFeedEntries(entries) {
titleLink.onclick = () => {
if (!entry.marked_read) {
// Mark as read in the background, don't wait for it
fetch(`/entries/${entry.id}/state`, {
fetch(`/entries/${entry.local_id}/state`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',