commit 175d647e47fbd9abec4134c708199ba8aa1ec682
parent 4c324e11606b25b8bec7396af56d5c252c3f04e8
Author: Jeremy Lin <jeremy.lin@gmail.com>
Date: Wed, 26 Aug 2020 01:27:38 -0700
Delete associated favorites when deleting a cipher or user
This prevents foreign key constraint violations.
Diffstat:
4 files changed, 107 insertions(+), 52 deletions(-)
diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs
@@ -2,7 +2,15 @@ use chrono::{NaiveDateTime, Utc};
use serde_json::Value;
use super::{
- Attachment, CollectionCipher, FolderCipher, Organization, User, UserOrgStatus, UserOrgType, UserOrganization,
+ Attachment,
+ CollectionCipher,
+ Favorite,
+ FolderCipher,
+ Organization,
+ User,
+ UserOrgStatus,
+ UserOrgType,
+ UserOrganization,
};
db_object! {
@@ -213,6 +221,7 @@ impl Cipher {
FolderCipher::delete_all_by_cipher(&self.uuid, conn)?;
CollectionCipher::delete_all_by_cipher(&self.uuid, conn)?;
Attachment::delete_all_by_cipher(&self.uuid, conn)?;
+ Favorite::delete_all_by_cipher(&self.uuid, conn)?;
db_run! { conn: {
diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
@@ -340,51 +349,14 @@ impl Cipher {
// Returns whether this cipher is a favorite of the specified user.
pub fn is_favorite(&self, user_uuid: &str, conn: &DbConn) -> bool {
- db_run!{ conn: {
- let query = favorites::table
- .filter(favorites::user_uuid.eq(user_uuid))
- .filter(favorites::cipher_uuid.eq(&self.uuid))
- .count();
-
- query.first::<i64>(conn).ok().unwrap_or(0) != 0
- }}
+ Favorite::is_favorite(&self.uuid, user_uuid, conn)
}
- // Updates whether this cipher is a favorite of the specified user.
+ // Sets whether this cipher is a favorite of the specified user.
pub fn set_favorite(&self, favorite: Option<bool>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
- if favorite.is_none() {
- // No change requested.
- return Ok(());
- }
-
- let (old, new) = (self.is_favorite(user_uuid, &conn), favorite.unwrap());
- match (old, new) {
- (false, true) => {
- User::update_uuid_revision(user_uuid, &conn);
- db_run!{ conn: {
- diesel::insert_into(favorites::table)
- .values((
- favorites::user_uuid.eq(user_uuid),
- favorites::cipher_uuid.eq(&self.uuid),
- ))
- .execute(conn)
- .map_res("Error adding favorite")
- }}
- }
- (true, false) => {
- User::update_uuid_revision(user_uuid, &conn);
- db_run!{ conn: {
- diesel::delete(
- favorites::table
- .filter(favorites::user_uuid.eq(user_uuid))
- .filter(favorites::cipher_uuid.eq(&self.uuid))
- )
- .execute(conn)
- .map_res("Error removing favorite")
- }}
- }
- // Otherwise, the favorite status is already what it should be.
- _ => Ok(())
+ match favorite {
+ None => Ok(()), // No change requested.
+ Some(status) => Favorite::set_favorite(status, &self.uuid, user_uuid, conn),
}
}
diff --git a/src/db/models/favorite.rs b/src/db/models/favorite.rs
@@ -0,0 +1,83 @@
+use super::{Cipher, User};
+
+db_object! {
+ #[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
+ #[table_name = "favorites"]
+ #[belongs_to(User, foreign_key = "user_uuid")]
+ #[belongs_to(Cipher, foreign_key = "cipher_uuid")]
+ #[primary_key(user_uuid, cipher_uuid)]
+ pub struct Favorite {
+ pub user_uuid: String,
+ pub cipher_uuid: String,
+ }
+}
+
+use crate::db::DbConn;
+
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
+impl Favorite {
+ // Returns whether the specified cipher is a favorite of the specified user.
+ pub fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool {
+ db_run!{ conn: {
+ let query = favorites::table
+ .filter(favorites::cipher_uuid.eq(cipher_uuid))
+ .filter(favorites::user_uuid.eq(user_uuid))
+ .count();
+
+ query.first::<i64>(conn).ok().unwrap_or(0) != 0
+ }}
+ }
+
+ // Sets whether the specified cipher is a favorite of the specified user.
+ pub fn set_favorite(favorite: bool, cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> EmptyResult {
+ let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, &conn), favorite);
+ match (old, new) {
+ (false, true) => {
+ User::update_uuid_revision(user_uuid, &conn);
+ db_run!{ conn: {
+ diesel::insert_into(favorites::table)
+ .values((
+ favorites::user_uuid.eq(user_uuid),
+ favorites::cipher_uuid.eq(cipher_uuid),
+ ))
+ .execute(conn)
+ .map_res("Error adding favorite")
+ }}
+ }
+ (true, false) => {
+ User::update_uuid_revision(user_uuid, &conn);
+ db_run!{ conn: {
+ diesel::delete(
+ favorites::table
+ .filter(favorites::user_uuid.eq(user_uuid))
+ .filter(favorites::cipher_uuid.eq(cipher_uuid))
+ )
+ .execute(conn)
+ .map_res("Error removing favorite")
+ }}
+ }
+ // Otherwise, the favorite status is already what it should be.
+ _ => Ok(())
+ }
+ }
+
+ // Delete all favorite entries associated with the specified cipher.
+ pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
+ db_run! { conn: {
+ diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid)))
+ .execute(conn)
+ .map_res("Error removing favorites by cipher")
+ }}
+ }
+
+ // Delete all favorite entries associated with the specified user.
+ pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
+ db_run! { conn: {
+ diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid)))
+ .execute(conn)
+ .map_res("Error removing favorites by user")
+ }}
+ }
+}
diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs
@@ -1,21 +1,21 @@
mod attachment;
mod cipher;
+mod collection;
mod device;
+mod favorite;
mod folder;
-mod user;
-
-mod collection;
+mod org_policy;
mod organization;
mod two_factor;
-mod org_policy;
+mod user;
pub use self::attachment::Attachment;
pub use self::cipher::Cipher;
pub use self::collection::{Collection, CollectionCipher, CollectionUser};
pub use self::device::Device;
+pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher};
-pub use self::organization::Organization;
-pub use self::organization::{UserOrgStatus, UserOrgType, UserOrganization};
+pub use self::org_policy::{OrgPolicy, OrgPolicyType};
+pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrganization};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::user::{Invitation, User};
-pub use self::org_policy::{OrgPolicy, OrgPolicyType};
-\ No newline at end of file
diff --git a/src/db/models/user.rs b/src/db/models/user.rs
@@ -128,7 +128,7 @@ impl User {
}
}
-use super::{Cipher, Device, Folder, TwoFactor, UserOrgType, UserOrganization};
+use super::{Cipher, Device, Favorite, Folder, TwoFactor, UserOrgType, UserOrganization};
use crate::db::DbConn;
use crate::api::EmptyResult;
@@ -205,6 +205,7 @@ impl User {
UserOrganization::delete_all_by_user(&self.uuid, conn)?;
Cipher::delete_all_by_user(&self.uuid, conn)?;
+ Favorite::delete_all_by_user(&self.uuid, conn)?;
Folder::delete_all_by_user(&self.uuid, conn)?;
Device::delete_all_by_user(&self.uuid, conn)?;
TwoFactor::delete_all_by_user(&self.uuid, conn)?;