Enforce uniqueness of actor ID

This commit is contained in:
silverpill 2022-04-20 13:47:04 +00:00
parent 0715b7d64f
commit 04e851025b
6 changed files with 43 additions and 2 deletions

View file

@ -23,7 +23,7 @@ Matrix chat: [#mitra:halogen.city](https://matrix.to/#/#mitra:halogen.city)
## Requirements
- Rust 1.54+
- PostgreSQL 10.2+
- PostgreSQL 12+
- IPFS node (optional, see [guide](./docs/ipfs.md))
- Ethereum node (optional)

View file

@ -0,0 +1 @@
ALTER TABLE actor_profile ADD COLUMN actor_id VARCHAR(200) UNIQUE GENERATED ALWAYS AS (actor_json ->> 'id') STORED;

View file

@ -12,6 +12,7 @@ CREATE TABLE actor_profile (
following_count INTEGER NOT NULL CHECK (following_count >= 0) DEFAULT 0,
post_count INTEGER NOT NULL CHECK (post_count >= 0) DEFAULT 0,
actor_json JSONB,
actor_id VARCHAR(200) UNIQUE GENERATED ALWAYS AS (actor_json ->> 'id') STORED,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
);

View file

@ -572,6 +572,7 @@ mod tests {
url: Some(parent_author_actor_url.to_string()),
..Default::default()
}),
actor_id: Some(parent_author_actor_id.to_string()),
..Default::default()
};
let parent = Post {

View file

@ -116,7 +116,7 @@ pub async fn get_profile_by_actor_id(
"
SELECT actor_profile
FROM actor_profile
WHERE actor_profile.actor_json ->> 'id' = $1
WHERE actor_id = $1
",
&[&actor_id],
).await?;
@ -471,6 +471,7 @@ pub async fn update_post_count(
#[cfg(test)]
mod tests {
use serde_json::json;
use serial_test::serial;
use crate::database::test_utils::create_test_database;
use crate::models::profiles::queries::create_profile;
@ -491,6 +492,38 @@ mod tests {
assert_eq!(profile.username, "test");
}
#[tokio::test]
#[serial]
async fn test_actor_id_unique() {
let db_client = create_test_database().await;
let actor_id = "https://example.com/users/test";
let create_actor_value = |actor_id| {
json!({
"id": actor_id,
"type": "Person",
"preferredUsername": "test",
"inbox": "https://test",
"outbox": "https://test",
"publicKey": {"id": "test", "owner": "test", "publicKeyPem": "test"},
})
};
let profile_data_1 = ProfileCreateData {
username: "test-1".to_string(),
acct: "test-1@example.com".to_string(),
actor_json: Some(create_actor_value(actor_id)),
..Default::default()
};
create_profile(&db_client, profile_data_1).await.unwrap();
let profile_data_2 = ProfileCreateData {
username: "test-2".to_string(),
acct: "test-2@example.com".to_string(),
actor_json: Some(create_actor_value(actor_id)),
..Default::default()
};
let error = create_profile(&db_client, profile_data_2).await.err().unwrap();
assert_eq!(error.to_string(), "profile");
}
#[tokio::test]
#[serial]
async fn test_delete_profile() {

View file

@ -86,6 +86,9 @@ pub struct DbActorProfile {
pub post_count: i32,
pub created_at: DateTime<Utc>,
pub actor_json: Option<Actor>,
// auto-generated database fields
pub actor_id: Option<String>,
}
impl DbActorProfile {
@ -94,6 +97,7 @@ impl DbActorProfile {
}
pub fn actor_id(&self, instance_url: &str) -> String {
// TODO: use actor_id field
match self.actor_json {
Some(ref actor) => actor.id.clone(),
None => get_actor_url(instance_url, &self.username),
@ -137,6 +141,7 @@ impl Default for DbActorProfile {
post_count: 0,
created_at: Utc::now(),
actor_json: None,
actor_id: None,
}
}
}