let secret = Secret::init(pool).await.unwrap();
let settings = &SETTINGS.to_owned();
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("Gerry9812".into())
let community = parse_lemmy_community(&context).await;
let community_id = community.id;
- let inserted_instance = Instance::create(context.pool(), "my_domain.tld")
+ let inserted_instance = Instance::read_or_create(context.pool(), "my_domain.tld".to_string())
.await
.unwrap();
source::{
actor_language::CommunityLanguage,
community::{Community, CommunityUpdateForm},
- instance::Instance,
},
traits::{ApubActor, Crud},
};
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<ApubCommunity, LemmyError> {
- let apub_id = group.id.inner().clone();
- let instance = Instance::create_from_actor_id(context.pool(), &apub_id).await?;
+ let instance_id = fetch_instance_actor_for_object(&group.id, context, request_counter).await?;
- let form = Group::into_insert_form(group.clone(), instance.id);
+ let form = Group::into_insert_form(group.clone(), instance_id);
let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?;
let community = Community::create(context.pool(), &form).await?;
.ok();
}
- fetch_instance_actor_for_object(community.actor_id(), context, request_counter).await;
-
Ok(community)
}
}
use chrono::NaiveDateTime;
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
use lemmy_db_schema::{
+ newtypes::InstanceId,
source::{
actor_language::SiteLanguage,
instance::Instance as DbInstance,
data: &Self::DataType,
_request_counter: &mut i32,
) -> Result<Self, LemmyError> {
- let apub_id = apub.id.inner().clone();
- let instance = DbInstance::create_from_actor_id(data.pool(), &apub_id).await?;
+ let domain = apub.id.inner().domain().expect("group id has domain");
+ let instance = DbInstance::read_or_create(data.pool(), domain.to_string()).await?;
let site_form = SiteInsertForm {
name: apub.name.clone(),
}
}
-/// try to fetch the instance actor (to make things like instance rules available)
-pub(in crate::objects) async fn fetch_instance_actor_for_object(
- object_id: Url,
+/// Try to fetch the instance actor (to make things like instance rules available).
+pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + Clone>(
+ object_id: &T,
context: &LemmyContext,
request_counter: &mut i32,
-) {
- // try to fetch the instance actor (to make things like instance rules available)
+) -> Result<InstanceId, LemmyError> {
+ let object_id: Url = object_id.clone().into();
let instance_id = Site::instance_actor_id_from_url(object_id);
let site = ObjectId::<ApubSite>::new(instance_id.clone())
.dereference(context, local_instance(context).await, request_counter)
.await;
- if let Err(e) = site {
- debug!("Failed to dereference site for {}: {}", instance_id, e);
+ match site {
+ Ok(s) => Ok(s.instance_id),
+ Err(e) => {
+ // Failed to fetch instance actor, its probably not a lemmy instance
+ debug!("Failed to dereference site for {}: {}", &instance_id, e);
+ let domain = instance_id.domain().expect("has domain");
+ Ok(
+ DbInstance::read_or_create(context.pool(), domain.to_string())
+ .await?
+ .id,
+ )
+ }
}
}
utils::{generate_outbox_url, local_site_opt_to_slur_regex},
};
use lemmy_db_schema::{
- source::{
- instance::Instance,
- person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
- },
+ source::person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
traits::{ApubActor, Crud},
utils::naive_now,
};
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<ApubPerson, LemmyError> {
- let apub_id = person.id.inner().clone();
- let instance = Instance::create_from_actor_id(context.pool(), &apub_id).await?;
+ let instance_id = fetch_instance_actor_for_object(&person.id, context, request_counter).await?;
let person_form = PersonInsertForm {
name: person.preferred_username,
inbox_url: Some(person.inbox.into()),
shared_inbox_url: person.endpoints.map(|e| e.shared_inbox.into()),
matrix_user_id: person.matrix_user_id,
- instance_id: instance.id,
+ instance_id,
};
let person = DbPerson::create(context.pool(), &person_form).await?;
- let actor_id = person.actor_id.clone().into();
- fetch_instance_actor_for_object(actor_id, context, request_counter).await;
-
Ok(person.into())
}
}
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("thommy_comment_agg".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("thommy_community_agg".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("thommy_user_agg".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("thommy_community_agg".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("thommy_site_agg".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let creator_form = PersonInsertForm::builder()
.name("activity_creator_ pm".into())
}
async fn create_test_site(pool: &DbPool) -> (Site, Instance) {
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let site_form = SiteInsertForm::builder()
.name("test site".to_string())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("terry".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("terrylake".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("bobbee".into())
for domain in list {
// Upsert all of these as instances
- let instance = Instance::create_conn(conn, &domain).await?;
+ let instance = Instance::read_or_create_with_conn(conn, domain).await?;
let form = FederationAllowListForm {
instance_id: instance.id,
for domain in list {
// Upsert all of these as instances
- let instance = Instance::create_conn(conn, &domain).await?;
+ let instance = Instance::read_or_create_with_conn(conn, domain).await?;
let form = FederationBlockListForm {
instance_id: instance.id,
};
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
use diesel_async::{AsyncPgConnection, RunQueryDsl};
-use url::Url;
impl Instance {
- async fn create_from_form_conn(
+ pub(crate) async fn read_or_create_with_conn(
conn: &mut AsyncPgConnection,
- form: &InstanceForm,
+ domain_: String,
) -> Result<Self, Error> {
- // Do upsert on domain name conflict
- insert_into(instance::table)
- .values(form)
- .on_conflict(instance::domain)
- .do_update()
- .set(form)
- .get_result::<Self>(conn)
- .await
+ use crate::schema::instance::domain;
+ // First try to read the instance row and return directly if found
+ let instance = instance::table
+ .filter(domain.eq(&domain_))
+ .first::<Self>(conn)
+ .await;
+ match instance {
+ Ok(i) => Ok(i),
+ Err(diesel::NotFound) => {
+ // Instance not in database yet, insert it
+ let form = InstanceForm::builder()
+ .domain(domain_)
+ .updated(Some(naive_now()))
+ .build();
+ insert_into(instance::table)
+ .values(&form)
+ // Necessary because this method may be called concurrently for the same domain. This
+ // could be handled with a transaction, but nested transactions arent allowed
+ .on_conflict(instance::domain)
+ .do_update()
+ .set(&form)
+ .get_result::<Self>(conn)
+ .await
+ }
+ e => e,
+ }
}
- pub async fn create(pool: &DbPool, domain: &str) -> Result<Self, Error> {
+
+ /// Attempt to read Instance column for the given domain. If it doesnt exist, insert a new one.
+ /// There is no need for update as the domain of an existing instance cant change.
+ pub async fn read_or_create(pool: &DbPool, domain: String) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
- Self::create_conn(conn, domain).await
- }
- pub async fn create_from_actor_id(pool: &DbPool, actor_id: &Url) -> Result<Self, Error> {
- let domain = actor_id.host_str().expect("actor id missing a domain");
- Self::create(pool, domain).await
- }
- pub async fn create_conn(conn: &mut AsyncPgConnection, domain: &str) -> Result<Self, Error> {
- let form = InstanceForm::builder()
- .domain(domain.to_string())
- .updated(Some(naive_now()))
- .build();
- Self::create_from_form_conn(conn, &form).await
+ Self::read_or_create_with_conn(conn, domain).await
}
pub async fn delete(pool: &DbPool, instance_id: InstanceId) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?;
.execute(conn)
.await
}
+ #[cfg(test)]
pub async fn delete_all(pool: &DbPool) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?;
diesel::delete(instance::table).execute(conn).await
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_mod = PersonInsertForm::builder()
.name("the mod".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("thommy prw".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("holly".into())
#[serial]
async fn follow() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let person_form_1 = PersonInsertForm::builder()
.name("erich".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("terrylake".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("jim".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let creator_form = PersonInsertForm::builder()
.name("creator_pm".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("timmy_crv".into())
}
async fn init_data(pool: &DbPool) -> Data {
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("timmy".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("timmy_prv".into())
}
async fn init_data(pool: &DbPool) -> Data {
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let person_name = "tegan".to_string();
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person_1 = PersonInsertForm::builder()
.name("timmy_mrv".into())
async fn test_crud() {
let pool = &build_db_pool_for_tests().await;
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let timmy_person_form = PersonInsertForm::builder()
.name("timmy_rav".into())
.expect("must have domain");
// Upsert this to the instance table
- let instance = Instance::create(pool, &domain).await?;
+ let instance = Instance::read_or_create(pool, domain).await?;
if let Some(setup) = &settings.setup {
let person_keypair = generate_actor_keypair()?;