Add API methods for retrieving followers and following lists
This commit is contained in:
parent
40958500c1
commit
3993c76c65
6 changed files with 175 additions and 7 deletions
|
@ -118,6 +118,9 @@ PATCH /api/v1/accounts/update_credentials
|
|||
GET /api/v1/accounts/relationships
|
||||
POST /api/v1/accounts/{account_id}/follow
|
||||
POST /api/v1/accounts/{account_id}/unfollow
|
||||
GET /api/v1/accounts/{account_id}/statuses
|
||||
GET /api/v1/accounts/{account_id}/followers
|
||||
GET /api/v1/accounts/{account_id}/following
|
||||
GET /api/v1/directory
|
||||
GET /api/v1/instance
|
||||
GET /api/v1/markers
|
||||
|
@ -145,7 +148,7 @@ GET /api/v1/statuses/{status_id}/signature
|
|||
POST /api/v1/statuses/{status_id}/token_minted
|
||||
```
|
||||
|
||||
[OpenAPI spec](./docs/openapi.yaml)
|
||||
[OpenAPI spec](./docs/openapi.yaml) (incomplete)
|
||||
|
||||
## CLI commands
|
||||
|
||||
|
|
|
@ -144,6 +144,66 @@ paths:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Status'
|
||||
/api/v1/accounts/{account_id}/followers:
|
||||
get:
|
||||
summary: Actors which follow the given actor.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/account_id'
|
||||
- name: max_id
|
||||
in: query
|
||||
description: Return results with relationship ID older than this value.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
- name: limit
|
||||
in: query
|
||||
description: Maximum number of results to return.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 40
|
||||
responses:
|
||||
200:
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
description: Profile list
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Account'
|
||||
404:
|
||||
description: Profile not found
|
||||
/api/v1/accounts/{account_id}/following:
|
||||
get:
|
||||
summary: Actors which the given actor is following.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/account_id'
|
||||
- name: max_id
|
||||
in: query
|
||||
description: Return results with relationship ID older than this value.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
- name: limit
|
||||
in: query
|
||||
description: Maximum number of results to return.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 40
|
||||
responses:
|
||||
200:
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
description: Profile list
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Account'
|
||||
404:
|
||||
description: Profile not found
|
||||
/api/v1/statuses/{status_id}:
|
||||
delete:
|
||||
summary: Delete post
|
||||
|
|
|
@ -174,6 +174,16 @@ impl AccountUpdateData {
|
|||
}
|
||||
}
|
||||
|
||||
fn default_page_size() -> i64 { 40 }
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FollowListQueryParams {
|
||||
pub max_id: Option<i32>,
|
||||
|
||||
#[serde(default = "default_page_size")]
|
||||
pub limit: i64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -31,6 +31,7 @@ use crate::models::relationships::queries::{
|
|||
follow,
|
||||
get_follow_request_by_path,
|
||||
get_followers,
|
||||
get_following,
|
||||
get_relationship,
|
||||
get_relationships,
|
||||
unfollow,
|
||||
|
@ -45,7 +46,12 @@ use crate::utils::crypto::{
|
|||
serialize_private_key,
|
||||
};
|
||||
use crate::utils::files::FileError;
|
||||
use super::types::{Account, AccountCreateData, AccountUpdateData};
|
||||
use super::types::{
|
||||
Account,
|
||||
AccountCreateData,
|
||||
AccountUpdateData,
|
||||
FollowListQueryParams,
|
||||
};
|
||||
|
||||
#[post("")]
|
||||
pub async fn create_account(
|
||||
|
@ -154,7 +160,7 @@ async fn update_credentials(
|
|||
// Federate
|
||||
let activity = create_activity_update_person(¤t_user, &config.instance_url())
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
let followers = get_followers(db_client, ¤t_user.id).await?;
|
||||
let followers = get_followers(db_client, ¤t_user.id, None, None).await?;
|
||||
let mut recipients: Vec<Actor> = Vec::new();
|
||||
for follower in followers {
|
||||
if let Some(remote_actor) = follower.actor_json {
|
||||
|
@ -298,6 +304,62 @@ async fn get_account_statuses(
|
|||
Ok(HttpResponse::Ok().json(statuses))
|
||||
}
|
||||
|
||||
#[get("/{account_id}/followers")]
|
||||
async fn get_account_followers(
|
||||
auth: BearerAuth,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<Pool>,
|
||||
web::Path(account_id): web::Path<Uuid>,
|
||||
query_params: web::Query<FollowListQueryParams>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||
if profile.id != current_user.id {
|
||||
// Social graph is hidden
|
||||
let accounts: Vec<Account> = vec![];
|
||||
return Ok(HttpResponse::Ok().json(accounts));
|
||||
};
|
||||
let followers = get_followers(
|
||||
db_client,
|
||||
&profile.id,
|
||||
query_params.max_id,
|
||||
Some(query_params.limit),
|
||||
).await?;
|
||||
let accounts: Vec<Account> = followers.into_iter()
|
||||
.map(|profile| Account::from_profile(profile, &config.instance_url()))
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(accounts))
|
||||
}
|
||||
|
||||
#[get("/{account_id}/following")]
|
||||
async fn get_account_following(
|
||||
auth: BearerAuth,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<Pool>,
|
||||
web::Path(account_id): web::Path<Uuid>,
|
||||
query_params: web::Query<FollowListQueryParams>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let profile = get_profile_by_id(db_client, &account_id).await?;
|
||||
if profile.id != current_user.id {
|
||||
// Social graph is hidden
|
||||
let accounts: Vec<Account> = vec![];
|
||||
return Ok(HttpResponse::Ok().json(accounts));
|
||||
};
|
||||
let following = get_following(
|
||||
db_client,
|
||||
&profile.id,
|
||||
query_params.max_id,
|
||||
Some(query_params.limit),
|
||||
).await?;
|
||||
let accounts: Vec<Account> = following.into_iter()
|
||||
.map(|profile| Account::from_profile(profile, &config.instance_url()))
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(accounts))
|
||||
}
|
||||
|
||||
pub fn account_api_scope() -> Scope {
|
||||
web::scope("/api/v1/accounts")
|
||||
// Routes without account ID
|
||||
|
@ -310,4 +372,6 @@ pub fn account_api_scope() -> Scope {
|
|||
.service(follow_account)
|
||||
.service(unfollow_account)
|
||||
.service(get_account_statuses)
|
||||
.service(get_account_followers)
|
||||
.service(get_account_following)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ pub async fn get_note_audience(
|
|||
current_user: &User,
|
||||
post: &Post,
|
||||
) -> Result<Vec<Actor>, DatabaseError> {
|
||||
let mut audience = get_followers(db_client, ¤t_user.id).await?;
|
||||
let mut audience = get_followers(db_client, ¤t_user.id, None, None).await?;
|
||||
if let Some(in_reply_to_id) = post.in_reply_to_id {
|
||||
// TODO: use post.in_reply_to ?
|
||||
let in_reply_to_author = get_post_author(db_client, &in_reply_to_id).await?;
|
||||
|
@ -51,7 +51,7 @@ pub async fn get_announce_audience(
|
|||
current_user: &User,
|
||||
post: &Post,
|
||||
) -> Result<Audience, DatabaseError> {
|
||||
let followers = get_followers(db_client, ¤t_user.id).await?;
|
||||
let followers = get_followers(db_client, ¤t_user.id, None, None).await?;
|
||||
let mut recipients: Vec<Actor> = Vec::new();
|
||||
for profile in followers {
|
||||
if let Some(remote_actor) = profile.actor_json {
|
||||
|
|
|
@ -242,6 +242,8 @@ pub async fn get_follow_request_by_path(
|
|||
pub async fn get_followers(
|
||||
db_client: &impl GenericClient,
|
||||
profile_id: &Uuid,
|
||||
max_relationship_id: Option<i32>,
|
||||
limit: Option<i64>,
|
||||
) -> Result<Vec<DbActorProfile>, DatabaseError> {
|
||||
let rows = db_client.query(
|
||||
"
|
||||
|
@ -249,10 +251,39 @@ pub async fn get_followers(
|
|||
FROM actor_profile
|
||||
JOIN relationship
|
||||
ON (actor_profile.id = relationship.source_id)
|
||||
WHERE relationship.target_id = $1
|
||||
WHERE
|
||||
relationship.target_id = $1
|
||||
AND ($2::integer IS NULL OR relationship.id < $2)
|
||||
ORDER BY relationship.id DESC
|
||||
LIMIT $3
|
||||
",
|
||||
&[&profile_id],
|
||||
&[&profile_id, &max_relationship_id, &limit],
|
||||
).await?;
|
||||
let profiles = rows.iter()
|
||||
.map(|row| row.try_get("actor_profile"))
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(profiles)
|
||||
}
|
||||
|
||||
pub async fn get_following(
|
||||
db_client: &impl GenericClient,
|
||||
profile_id: &Uuid,
|
||||
max_relationship_id: Option<i32>,
|
||||
limit: Option<i64>,
|
||||
) -> Result<Vec<DbActorProfile>, DatabaseError> {
|
||||
let rows = db_client.query(
|
||||
"
|
||||
SELECT actor_profile
|
||||
FROM actor_profile
|
||||
JOIN relationship
|
||||
ON (actor_profile.id = relationship.target_id)
|
||||
WHERE
|
||||
relationship.source_id = $1
|
||||
AND ($2::integer IS NULL OR relationship.id < $2)
|
||||
ORDER BY relationship.id DESC
|
||||
LIMIT $3
|
||||
",
|
||||
&[&profile_id, &max_relationship_id, &limit],
|
||||
).await?;
|
||||
let profiles = rows.iter()
|
||||
.map(|row| row.try_get("actor_profile"))
|
||||
|
|
Loading…
Reference in a new issue