1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2024-11-22 12:21:09 +00:00

use newtypes for all ids

This commit is contained in:
Felix Ableitner 2024-11-11 23:13:35 +01:00
parent 7d8954ecd1
commit 639ca1704f
14 changed files with 102 additions and 54 deletions

View file

@ -1,2 +1 @@
ALTER TABLE conflict DROP CONSTRAINT conflict_creator_id_fkey;
ALTER TABLE conflict ADD CONSTRAINT conflict_creator_id_fkey FOREIGN KEY (creator_id) REFERENCES local_user(id) ON UPDATE CASCADE ON DELETE CASCADE;
select 1;

View file

@ -7,7 +7,13 @@ use crate::{
error::MyResult,
federation::objects::edits_collection::DbEditCollection,
},
common::{ArticleView, DbArticle, DbEdit, EditVersion},
common::{
newtypes::{ArticleId, InstanceId},
ArticleView,
DbArticle,
DbEdit,
EditVersion,
},
};
use activitypub_federation::fetch::{collection_id::CollectionId, object_id::ObjectId};
use diesel::{
@ -29,7 +35,7 @@ pub struct DbArticleForm {
pub title: String,
pub text: String,
pub ap_id: ObjectId<DbArticle>,
pub instance_id: i32,
pub instance_id: InstanceId,
pub local: bool,
pub protected: bool,
}
@ -59,26 +65,26 @@ impl DbArticle {
.get_result(conn.deref_mut())?)
}
pub fn update_text(id: i32, text: &str, data: &IbisData) -> MyResult<Self> {
pub fn update_text(id: ArticleId, text: &str, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::text.eq(text))
.get_result::<Self>(conn.deref_mut())?)
}
pub fn update_protected(id: i32, locked: bool, data: &IbisData) -> MyResult<Self> {
pub fn update_protected(id: ArticleId, locked: bool, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::protected.eq(locked))
.get_result::<Self>(conn.deref_mut())?)
}
pub fn read(id: i32, data: &IbisData) -> MyResult<Self> {
pub fn read(id: ArticleId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
Ok(article::table.find(id).get_result(conn.deref_mut())?)
}
pub fn read_view(id: i32, data: &IbisData) -> MyResult<ArticleView> {
pub fn read_view(id: ArticleId, data: &IbisData) -> MyResult<ArticleView> {
let mut conn = data.db_pool.get()?;
let article: DbArticle = { article::table.find(id).get_result(conn.deref_mut())? };
let latest_version = article.latest_edit_version(data)?;
@ -139,7 +145,7 @@ impl DbArticle {
/// TODO: Should get rid of only_local param and rely on instance_id
pub fn read_all(
only_local: Option<bool>,
instance_id: Option<i32>,
instance_id: Option<InstanceId>,
data: &IbisData,
) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?;

View file

@ -5,9 +5,15 @@ use crate::{
federation::activities::submit_article_update,
utils::generate_article_version,
},
common::{ApiConflict, DbArticle, DbEdit, DbPerson, EditVersion},
common::{
newtypes::{ArticleId, ConflictId, PersonId},
ApiConflict,
DbArticle,
DbEdit,
DbPerson,
EditVersion,
},
};
use crate::common::newtypes::PersonId;
use activitypub_federation::config::Data;
use diesel::{
delete,
@ -29,12 +35,12 @@ use std::ops::DerefMut;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Queryable, Selectable, Identifiable)]
#[diesel(table_name = conflict, check_for_backend(diesel::pg::Pg), belongs_to(DbArticle, foreign_key = article_id))]
pub struct DbConflict {
pub id: i32,
pub id: ConflictId,
pub hash: EditVersion,
pub diff: String,
pub summary: String,
pub creator_id: PersonId,
pub article_id: i32,
pub article_id: ArticleId,
pub previous_version_id: EditVersion,
}
@ -45,7 +51,7 @@ pub struct DbConflictForm {
pub diff: String,
pub summary: String,
pub creator_id: PersonId,
pub article_id: i32,
pub article_id: ArticleId,
pub previous_version_id: EditVersion,
}
@ -65,7 +71,7 @@ impl DbConflict {
}
/// Delete a merge conflict after it is resolved.
pub fn delete(id: i32, data: &IbisData) -> MyResult<Self> {
pub fn delete(id: ConflictId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
Ok(delete(conflict::table.find(id)).get_result(conn.deref_mut())?)
}

View file

@ -4,14 +4,19 @@ use crate::{
error::MyResult,
IbisData,
},
common::{DbArticle, DbEdit, EditVersion, EditView},
common::{
newtypes::{ArticleId, PersonId},
DbArticle,
DbEdit,
EditVersion,
EditView,
},
};
use activitypub_federation::fetch::object_id::ObjectId;
use chrono::{DateTime, Utc};
use diesel::{insert_into, AsChangeset, ExpressionMethods, Insertable, QueryDsl, RunQueryDsl};
use diffy::create_patch;
use std::ops::DerefMut;
use crate::common::newtypes::PersonId;
#[derive(Debug, Clone, Insertable, AsChangeset)]
#[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))]
@ -21,7 +26,7 @@ pub struct DbEditForm {
pub ap_id: ObjectId<DbEdit>,
pub diff: String,
pub summary: String,
pub article_id: i32,
pub article_id: ArticleId,
pub previous_version_id: EditVersion,
pub created: DateTime<Utc>,
}

View file

@ -10,7 +10,7 @@ use crate::{
instance_collection::DbInstanceCollection,
},
},
common::{DbInstance, DbPerson, InstanceView},
common::{newtypes::InstanceId, DbInstance, DbPerson, InstanceView},
};
use activitypub_federation::{
config::Data,
@ -54,7 +54,7 @@ impl DbInstance {
.get_result(conn.deref_mut())?)
}
pub fn read(id: i32, data: &IbisData) -> MyResult<Self> {
pub fn read(id: InstanceId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
Ok(instance::table.find(id).get_result(conn.deref_mut())?)
}
@ -76,7 +76,7 @@ impl DbInstance {
.get_result(conn.deref_mut())?)
}
pub fn read_view(id: Option<i32>, data: &Data<IbisData>) -> MyResult<InstanceView> {
pub fn read_view(id: Option<InstanceId>, data: &Data<IbisData>) -> MyResult<InstanceView> {
let instance = match id {
Some(id) => DbInstance::read(id, data),
None => DbInstance::read_local_instance(data),
@ -113,7 +113,7 @@ impl DbInstance {
Ok(())
}
pub fn read_followers(id_: i32, data: &IbisData) -> MyResult<Vec<DbPerson>> {
pub fn read_followers(id_: InstanceId, data: &IbisData) -> MyResult<Vec<DbPerson>> {
use crate::backend::database::schema::person;
use instance_follow::dsl::{follower_id, instance_id};
let mut conn = data.db_pool.get()?;

View file

@ -7,6 +7,7 @@ use crate::{
error::MyResult,
},
common::{
newtypes::PersonId,
utils::http_protocol_str,
DbInstance,
DbLocalUser,
@ -14,7 +15,6 @@ use crate::{
LocalUserView,
},
};
use crate::common::newtypes::PersonId;
use activitypub_federation::{
config::Data,
fetch::object_id::ObjectId,

View file

@ -7,11 +7,16 @@ use crate::{
update_remote_article::UpdateRemoteArticle,
},
},
common::{DbArticle, DbEdit, DbInstance, EditVersion},
common::{
newtypes::{EditId, PersonId},
DbArticle,
DbEdit,
DbInstance,
EditVersion,
},
};
use activitypub_federation::config::Data;
use chrono::Utc;
use crate::common::newtypes::PersonId;
pub mod accept;
pub mod create_article;
@ -43,7 +48,7 @@ pub async fn submit_article_update(
} else {
// dont insert edit into db, might be invalid in case of conflict
let edit = DbEdit {
id: -1,
id: EditId(-1),
creator_id,
hash: form.hash,
ap_id: form.ap_id,

View file

@ -46,7 +46,11 @@ pub fn generate_article_version(edits: &Vec<EditView>, version: &EditVersion) ->
#[cfg(test)]
mod test {
use super::*;
use crate::common::{newtypes::PersonId, DbEdit, DbPerson};
use crate::common::{
newtypes::{ArticleId, EditId, PersonId},
DbEdit,
DbPerson,
};
use activitypub_federation::fetch::object_id::ObjectId;
use chrono::Utc;
use diffy::create_patch;
@ -56,13 +60,13 @@ mod test {
let diff = create_patch(a, b).to_string();
Ok(EditView {
edit: DbEdit {
id: 0,
id: EditId(0),
creator_id: PersonId(0),
hash: EditVersion::new(&diff),
ap_id: ObjectId::parse("http://example.com")?,
diff,
summary: String::new(),
article_id: 0,
article_id: ArticleId(0),
previous_version_id: Default::default(),
created: Utc::now(),
},

View file

@ -3,7 +3,7 @@ pub mod utils;
pub mod validation;
use chrono::{DateTime, Utc};
use newtypes::PersonId;
use newtypes::{ArticleId, ConflictId, EditId, InstanceId, PersonId};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use url::Url;
@ -26,13 +26,13 @@ pub const MAIN_PAGE_NAME: &str = "Main_Page";
pub struct GetArticleForm {
pub title: Option<String>,
pub domain: Option<String>,
pub id: Option<i32>,
pub id: Option<ArticleId>,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct ListArticlesForm {
pub only_local: Option<bool>,
pub instance_id: Option<i32>,
pub instance_id: Option<InstanceId>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@ -48,14 +48,14 @@ pub struct ArticleView {
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))]
#[cfg_attr(feature = "ssr", diesel(table_name = article, check_for_backend(diesel::pg::Pg), belongs_to(DbInstance, foreign_key = instance_id)))]
pub struct DbArticle {
pub id: i32,
pub id: ArticleId,
pub title: String,
pub text: String,
#[cfg(feature = "ssr")]
pub ap_id: ObjectId<DbArticle>,
#[cfg(not(feature = "ssr"))]
pub ap_id: String,
pub instance_id: i32,
pub instance_id: InstanceId,
pub local: bool,
pub protected: bool,
}
@ -67,7 +67,7 @@ pub struct DbArticle {
pub struct DbEdit {
// TODO: we could use hash as primary key, but that gives errors on forking because
// the same edit is used for multiple articles
pub id: i32,
pub id: EditId,
#[serde(skip)]
pub creator_id: PersonId,
/// UUID built from sha224 hash of diff
@ -78,7 +78,7 @@ pub struct DbEdit {
pub ap_id: String,
pub diff: String,
pub summary: String,
pub article_id: i32,
pub article_id: ArticleId,
/// First edit of an article always has `EditVersion::default()` here
pub previous_version_id: EditVersion,
pub created: DateTime<Utc>,
@ -145,10 +145,10 @@ pub struct LocalUserView {
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))]
#[cfg_attr(feature = "ssr", diesel(table_name = local_user, check_for_backend(diesel::pg::Pg)))]
pub struct DbLocalUser {
pub id: i32,
pub id: InstanceId,
#[serde(skip)]
pub password_encrypted: String,
pub person_id: i32,
pub person_id: PersonId,
pub admin: bool,
}
@ -189,7 +189,7 @@ pub struct CreateArticleForm {
#[derive(Deserialize, Serialize, Debug)]
pub struct EditArticleForm {
/// Id of the article to edit
pub article_id: i32,
pub article_id: ArticleId,
/// Full, new text of the article. A diff against `previous_version` is generated on the backend
/// side to handle conflicts.
pub new_text: String,
@ -199,29 +199,29 @@ pub struct EditArticleForm {
/// [ApiConflict.previous_version]
pub previous_version_id: EditVersion,
/// If you are resolving a conflict, pass the id to delete conflict from the database
pub resolve_conflict_id: Option<i32>,
pub resolve_conflict_id: Option<ConflictId>,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct ProtectArticleForm {
pub article_id: i32,
pub article_id: ArticleId,
pub protected: bool,
}
#[derive(Deserialize, Serialize)]
pub struct ForkArticleForm {
pub article_id: i32,
pub article_id: ArticleId,
pub new_title: String,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct GetInstance {
pub id: Option<i32>,
pub id: Option<InstanceId>,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct FollowInstance {
pub id: i32,
pub id: InstanceId,
}
#[derive(Deserialize, Serialize, Clone)]
@ -236,7 +236,7 @@ pub struct ResolveObject {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ApiConflict {
pub id: i32,
pub id: ConflictId,
pub hash: EditVersion,
pub three_way_merge: String,
pub summary: String,
@ -248,7 +248,7 @@ pub struct ApiConflict {
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))]
#[cfg_attr(feature = "ssr", diesel(table_name = instance, check_for_backend(diesel::pg::Pg)))]
pub struct DbInstance {
pub id: i32,
pub id: InstanceId,
pub domain: String,
#[cfg(feature = "ssr")]
pub ap_id: ObjectId<DbInstance>,

23
src/common/newtypes.rs Normal file
View file

@ -0,0 +1,23 @@
#[cfg(feature = "ssr")]
use diesel_derive_newtype::DieselNewType;
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct PersonId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct ArticleId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct EditId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct InstanceId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct ConflictId(pub i32);

View file

@ -1,5 +1,5 @@
use crate::{
common::{DbInstance, FollowInstance},
common::{newtypes::InstanceId, DbInstance, FollowInstance},
frontend::app::GlobalState,
};
use leptos::{component, *};
@ -7,7 +7,7 @@ use leptos::{component, *};
#[component]
pub fn InstanceFollowButton(instance: DbInstance) -> impl IntoView {
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
let follow_action = create_action(move |instance_id: &i32| {
let follow_action = create_action(move |instance_id: &InstanceId| {
let instance_id = *instance_id;
async move {
let form = FollowInstance { id: instance_id };

View file

@ -1,5 +1,5 @@
use crate::{
common::{ForkArticleForm, ProtectArticleForm},
common::{newtypes::ArticleId, ForkArticleForm, ProtectArticleForm},
frontend::{
app::GlobalState,
article_link,
@ -18,7 +18,7 @@ pub fn ArticleActions() -> impl IntoView {
let (new_title, set_new_title) = create_signal(String::new());
let (fork_response, set_fork_response) = create_signal(Option::<DbArticle>::None);
let (error, set_error) = create_signal(None::<String>);
let fork_action = create_action(move |(article_id, new_title): &(i32, String)| {
let fork_action = create_action(move |(article_id, new_title): &(ArticleId, String)| {
let params = ForkArticleForm {
article_id: *article_id,
new_title: new_title.to_string(),
@ -34,7 +34,7 @@ pub fn ArticleActions() -> impl IntoView {
}
}
});
let protect_action = create_action(move |(id, protected): &(i32, bool)| {
let protect_action = create_action(move |(id, protected): &(ArticleId, bool)| {
let params = ProtectArticleForm {
article_id: *id,
protected: !protected,

View file

@ -1,5 +1,5 @@
use crate::{
common::{ApiConflict, ArticleView, EditArticleForm},
common::{newtypes::ConflictId, ApiConflict, ArticleView, EditArticleForm},
frontend::{
app::GlobalState,
components::{
@ -32,7 +32,7 @@ pub fn EditArticle() -> impl IntoView {
let conflict_id = move || use_params_map().get_untracked().get("conflict_id").cloned();
if let Some(conflict_id) = conflict_id() {
create_action(move |conflict_id: &String| {
let conflict_id: i32 = conflict_id.parse().unwrap();
let conflict_id = ConflictId(conflict_id.parse().unwrap());
async move {
let conflict = GlobalState::api_client()
.get_conflicts()

View file

@ -21,7 +21,7 @@ pub fn Conflicts() -> impl IntoView {
let link = format!(
"{}/edit/{}",
article_link(&c.article),
c.id,
c.id.0,
);
view! {
<li>