Support search by DID

This commit is contained in:
silverpill 2022-08-04 17:51:42 +00:00
parent 46526fd4be
commit 21df4fa35c
2 changed files with 73 additions and 25 deletions

View file

@ -1,3 +1,5 @@
use std::str::FromStr;
use regex::Regex; use regex::Regex;
use tokio_postgres::GenericClient; use tokio_postgres::GenericClient;
use url::Url; use url::Url;
@ -9,12 +11,14 @@ use crate::activitypub::fetcher::helpers::{
}; };
use crate::config::Config; use crate::config::Config;
use crate::errors::{ValidationError, HttpError}; use crate::errors::{ValidationError, HttpError};
use crate::ethereum::identity::DidPkh;
use crate::mastodon_api::accounts::types::Account; use crate::mastodon_api::accounts::types::Account;
use crate::mastodon_api::statuses::helpers::build_status_list; use crate::mastodon_api::statuses::helpers::build_status_list;
use crate::models::posts::helpers::can_view_post; use crate::models::posts::helpers::can_view_post;
use crate::models::posts::types::Post; use crate::models::posts::types::Post;
use crate::models::profiles::queries::{ use crate::models::profiles::queries::{
search_profile, search_profile,
search_profile_by_did,
search_profile_by_wallet_address, search_profile_by_wallet_address,
}; };
use crate::models::profiles::types::DbActorProfile; use crate::models::profiles::types::DbActorProfile;
@ -28,6 +32,7 @@ enum SearchQuery {
ProfileQuery(String, Option<String>), ProfileQuery(String, Option<String>),
Url(String), Url(String),
WalletAddress(String), WalletAddress(String),
Did(DidPkh),
Unknown, Unknown,
} }
@ -48,6 +53,10 @@ fn parse_profile_query(query: &str) ->
fn parse_search_query(search_query: &str) -> SearchQuery { fn parse_search_query(search_query: &str) -> SearchQuery {
let search_query = search_query.trim(); let search_query = search_query.trim();
// DID is a valid URI so it should be tried before Url::parse
if let Ok(did) = DidPkh::from_str(search_query) {
return SearchQuery::Did(did);
};
if Url::parse(search_query).is_ok() { if Url::parse(search_query).is_ok() {
return SearchQuery::Url(search_query.to_string()); return SearchQuery::Url(search_query.to_string());
}; };
@ -163,6 +172,13 @@ pub async fn search(
false, false,
).await?; ).await?;
}, },
SearchQuery::Did(did) => {
profiles = search_profile_by_did(
db_client,
&did,
false,
).await?;
},
SearchQuery::Unknown => (), // ignore SearchQuery::Unknown => (), // ignore
}; };
let accounts: Vec<Account> = profiles.into_iter() let accounts: Vec<Account> = profiles.into_iter()

View file

@ -2,6 +2,7 @@ use tokio_postgres::GenericClient;
use uuid::Uuid; use uuid::Uuid;
use crate::database::catch_unique_violation; use crate::database::catch_unique_violation;
use crate::database::query_macro::query;
use crate::errors::DatabaseError; use crate::errors::DatabaseError;
use crate::ethereum::identity::DidPkh; use crate::ethereum::identity::DidPkh;
use crate::models::cleanup::{ use crate::models::cleanup::{
@ -377,19 +378,13 @@ pub async fn search_profile(
Ok(profiles) Ok(profiles)
} }
pub async fn search_profile_by_wallet_address( pub async fn search_profile_by_did(
db_client: &impl GenericClient, db_client: &impl GenericClient,
currency: &Currency, did: &DidPkh,
wallet_address: &str,
prefer_verified: bool, prefer_verified: bool,
) -> Result<Vec<DbActorProfile>, DatabaseError> { ) -> Result<Vec<DbActorProfile>, DatabaseError> {
let field_name = currency.field_name(); let did_str = did.to_string();
let did_str = DidPkh::from_address(currency, wallet_address).to_string(); let identity_proof_query =
// If currency is Ethereum,
// search over extra fields must be case insensitive.
// This query does not scan user_account.wallet_address because
// login addresses are private by default.
let rows = db_client.query(
" "
SELECT actor_profile, TRUE AS is_verified SELECT actor_profile, TRUE AS is_verified
FROM actor_profile FROM actor_profile
@ -397,22 +392,49 @@ pub async fn search_profile_by_wallet_address(
EXISTS ( EXISTS (
SELECT 1 SELECT 1
FROM jsonb_array_elements(actor_profile.identity_proofs) AS proof FROM jsonb_array_elements(actor_profile.identity_proofs) AS proof
WHERE proof ->> 'issuer' = $3 WHERE proof ->> 'issuer' = $did
) )
UNION ALL ";
SELECT actor_profile, FALSE let rows = if let Some(currency) = did.currency() {
FROM actor_profile // If currency is Ethereum,
WHERE // search over extra fields must be case insensitive.
EXISTS ( #[allow(unreachable_patterns)]
SELECT 1 let value_op = match currency {
FROM jsonb_array_elements(actor_profile.extra_fields) AS field Currency::Ethereum => "ILIKE",
WHERE _ => "LIKE",
field ->> 'name' ILIKE $1 };
AND field ->> 'value' ILIKE $2 // This query does not scan user_account.wallet_address because
) // login addresses are private by default.
", let statement = format!(
&[&field_name, &wallet_address, &did_str], "
).await?; {identity_proof_query}
UNION ALL
SELECT actor_profile, FALSE
FROM actor_profile
WHERE
EXISTS (
SELECT 1
FROM jsonb_array_elements(actor_profile.extra_fields) AS field
WHERE
field ->> 'name' ILIKE $field_name
AND field ->> 'value' {value_op} $field_value
)
",
identity_proof_query=identity_proof_query,
value_op=value_op,
);
let field_name = currency.field_name();
let query = query!(
&statement,
did=did_str,
field_name=field_name,
field_value=did.address,
)?;
db_client.query(query.sql(), query.parameters()).await?
} else {
let query = query!(identity_proof_query, did=did_str)?;
db_client.query(query.sql(), query.parameters()).await?
};
let mut verified = vec![]; let mut verified = vec![];
let mut unverified = vec![]; let mut unverified = vec![];
for row in rows { for row in rows {
@ -432,6 +454,16 @@ pub async fn search_profile_by_wallet_address(
Ok(results) Ok(results)
} }
pub async fn search_profile_by_wallet_address(
db_client: &impl GenericClient,
currency: &Currency,
wallet_address: &str,
prefer_verified: bool,
) -> Result<Vec<DbActorProfile>, DatabaseError> {
let did = DidPkh::from_address(currency, wallet_address);
search_profile_by_did(db_client, &did, prefer_verified).await
}
pub async fn update_follower_count( pub async fn update_follower_count(
db_client: &impl GenericClient, db_client: &impl GenericClient,
profile_id: &Uuid, profile_id: &Uuid,