Enforce uniqueness of actor ID
This commit is contained in:
parent
0715b7d64f
commit
04e851025b
6 changed files with 43 additions and 2 deletions
|
@ -23,7 +23,7 @@ Matrix chat: [#mitra:halogen.city](https://matrix.to/#/#mitra:halogen.city)
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Rust 1.54+
|
- Rust 1.54+
|
||||||
- PostgreSQL 10.2+
|
- PostgreSQL 12+
|
||||||
- IPFS node (optional, see [guide](./docs/ipfs.md))
|
- IPFS node (optional, see [guide](./docs/ipfs.md))
|
||||||
- Ethereum node (optional)
|
- Ethereum node (optional)
|
||||||
|
|
||||||
|
|
1
migrations/V0022__actor_profile__add_actor_id.sql
Normal file
1
migrations/V0022__actor_profile__add_actor_id.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE actor_profile ADD COLUMN actor_id VARCHAR(200) UNIQUE GENERATED ALWAYS AS (actor_json ->> 'id') STORED;
|
|
@ -12,6 +12,7 @@ CREATE TABLE actor_profile (
|
||||||
following_count INTEGER NOT NULL CHECK (following_count >= 0) DEFAULT 0,
|
following_count INTEGER NOT NULL CHECK (following_count >= 0) DEFAULT 0,
|
||||||
post_count INTEGER NOT NULL CHECK (post_count >= 0) DEFAULT 0,
|
post_count INTEGER NOT NULL CHECK (post_count >= 0) DEFAULT 0,
|
||||||
actor_json JSONB,
|
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()
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -572,6 +572,7 @@ mod tests {
|
||||||
url: Some(parent_author_actor_url.to_string()),
|
url: Some(parent_author_actor_url.to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
actor_id: Some(parent_author_actor_id.to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let parent = Post {
|
let parent = Post {
|
||||||
|
|
|
@ -116,7 +116,7 @@ pub async fn get_profile_by_actor_id(
|
||||||
"
|
"
|
||||||
SELECT actor_profile
|
SELECT actor_profile
|
||||||
FROM actor_profile
|
FROM actor_profile
|
||||||
WHERE actor_profile.actor_json ->> 'id' = $1
|
WHERE actor_id = $1
|
||||||
",
|
",
|
||||||
&[&actor_id],
|
&[&actor_id],
|
||||||
).await?;
|
).await?;
|
||||||
|
@ -471,6 +471,7 @@ pub async fn update_post_count(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use serde_json::json;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use crate::database::test_utils::create_test_database;
|
use crate::database::test_utils::create_test_database;
|
||||||
use crate::models::profiles::queries::create_profile;
|
use crate::models::profiles::queries::create_profile;
|
||||||
|
@ -491,6 +492,38 @@ mod tests {
|
||||||
assert_eq!(profile.username, "test");
|
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]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_delete_profile() {
|
async fn test_delete_profile() {
|
||||||
|
|
|
@ -86,6 +86,9 @@ pub struct DbActorProfile {
|
||||||
pub post_count: i32,
|
pub post_count: i32,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub actor_json: Option<Actor>,
|
pub actor_json: Option<Actor>,
|
||||||
|
|
||||||
|
// auto-generated database fields
|
||||||
|
pub actor_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbActorProfile {
|
impl DbActorProfile {
|
||||||
|
@ -94,6 +97,7 @@ impl DbActorProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn actor_id(&self, instance_url: &str) -> String {
|
pub fn actor_id(&self, instance_url: &str) -> String {
|
||||||
|
// TODO: use actor_id field
|
||||||
match self.actor_json {
|
match self.actor_json {
|
||||||
Some(ref actor) => actor.id.clone(),
|
Some(ref actor) => actor.id.clone(),
|
||||||
None => get_actor_url(instance_url, &self.username),
|
None => get_actor_url(instance_url, &self.username),
|
||||||
|
@ -137,6 +141,7 @@ impl Default for DbActorProfile {
|
||||||
post_count: 0,
|
post_count: 0,
|
||||||
created_at: Utc::now(),
|
created_at: Utc::now(),
|
||||||
actor_json: None,
|
actor_json: None,
|
||||||
|
actor_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue