Add API method for listing incoming subscriptions
This commit is contained in:
parent
bdcdb06c51
commit
1721eb4a88
6 changed files with 154 additions and 2 deletions
|
@ -342,6 +342,36 @@ paths:
|
|||
example: '<https://example.org/api/v1/accounts/1/following?limit=40&max_id=7628164>; rel="next"'
|
||||
404:
|
||||
description: Profile not found
|
||||
/api/v1/accounts/{account_id}/subscribers:
|
||||
get:
|
||||
summary: Subscriptions to the given actor.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/account_id'
|
||||
- name: max_id
|
||||
in: query
|
||||
description: Return results with subscription 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: Subscription list
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Subscription'
|
||||
404:
|
||||
description: Profile not found
|
||||
/api/v1/accounts/{account_id}/follow:
|
||||
post:
|
||||
summary: Follow the given actor.
|
||||
|
@ -997,6 +1027,18 @@ components:
|
|||
type: string
|
||||
nullable: true
|
||||
example: '0x5fe80cdea7f...'
|
||||
Subscription:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: Subscription ID.
|
||||
type: number
|
||||
sender:
|
||||
$ref: '#/components/schemas/Account'
|
||||
sender_address:
|
||||
description: Sender address.
|
||||
type: string
|
||||
example: '0xd8da6bf...'
|
||||
Tag:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::models::profiles::types::{
|
|||
ProfileUpdateData,
|
||||
};
|
||||
use crate::models::profiles::validators::validate_username;
|
||||
use crate::models::subscriptions::types::Subscription;
|
||||
use crate::models::users::types::{
|
||||
validate_local_username,
|
||||
User,
|
||||
|
@ -334,6 +335,27 @@ pub struct FollowListQueryParams {
|
|||
pub limit: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ApiSubscription {
|
||||
pub id: i32,
|
||||
pub sender: Account,
|
||||
pub sender_address: String,
|
||||
}
|
||||
|
||||
impl ApiSubscription {
|
||||
pub fn from_subscription(
|
||||
instance_url: &str,
|
||||
subscription: Subscription,
|
||||
) -> Self {
|
||||
let sender = Account::from_profile(subscription.sender, instance_url);
|
||||
Self {
|
||||
id: subscription.id,
|
||||
sender,
|
||||
sender_address: subscription.sender_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -52,6 +52,7 @@ use crate::models::relationships::queries::{
|
|||
show_reposts,
|
||||
unfollow,
|
||||
};
|
||||
use crate::models::subscriptions::queries::get_incoming_subscriptions;
|
||||
use crate::models::users::queries::{
|
||||
is_valid_invite_code,
|
||||
create_user,
|
||||
|
@ -76,6 +77,7 @@ use super::types::{
|
|||
SearchDidQueryParams,
|
||||
StatusListQueryParams,
|
||||
SubscriptionQueryParams,
|
||||
ApiSubscription,
|
||||
};
|
||||
|
||||
#[post("")]
|
||||
|
@ -584,6 +586,36 @@ async fn get_account_following(
|
|||
Ok(response)
|
||||
}
|
||||
|
||||
#[get("/{account_id}/subscribers")]
|
||||
async fn get_account_subscribers(
|
||||
auth: BearerAuth,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<Pool>,
|
||||
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 subscriptions: Vec<ApiSubscription> = vec![];
|
||||
return Ok(HttpResponse::Ok().json(subscriptions));
|
||||
};
|
||||
let instance_url = config.instance_url();
|
||||
let subscriptions: Vec<ApiSubscription> = get_incoming_subscriptions(
|
||||
db_client,
|
||||
&profile.id,
|
||||
query_params.max_id,
|
||||
query_params.limit.into(),
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|item| ApiSubscription::from_subscription(&instance_url, item))
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(subscriptions))
|
||||
}
|
||||
|
||||
pub fn account_api_scope() -> Scope {
|
||||
web::scope("/api/v1/accounts")
|
||||
// Routes without account ID
|
||||
|
@ -603,4 +635,5 @@ pub fn account_api_scope() -> Scope {
|
|||
.service(get_account_statuses)
|
||||
.service(get_account_followers)
|
||||
.service(get_account_following)
|
||||
.service(get_account_subscribers)
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
pub mod queries;
|
||||
mod types;
|
||||
pub mod types;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use tokio_postgres::GenericClient;
|
||||
use uuid::Uuid;
|
||||
|
@ -6,7 +8,7 @@ use crate::database::catch_unique_violation;
|
|||
use crate::errors::DatabaseError;
|
||||
use crate::models::relationships::queries::{subscribe, subscribe_opt};
|
||||
use crate::models::relationships::types::RelationshipType;
|
||||
use super::types::DbSubscription;
|
||||
use super::types::{DbSubscription, Subscription};
|
||||
|
||||
pub async fn create_subscription(
|
||||
db_client: &mut impl GenericClient,
|
||||
|
@ -111,3 +113,29 @@ pub async fn get_expired_subscriptions(
|
|||
.collect::<Result<_, _>>()?;
|
||||
Ok(subscriptions)
|
||||
}
|
||||
|
||||
pub async fn get_incoming_subscriptions(
|
||||
db_client: &impl GenericClient,
|
||||
recipient_id: &Uuid,
|
||||
max_subscription_id: Option<i32>,
|
||||
limit: i64,
|
||||
) -> Result<Vec<Subscription>, DatabaseError> {
|
||||
let rows = db_client.query(
|
||||
"
|
||||
SELECT subscription, actor_profile AS sender
|
||||
FROM actor_profile
|
||||
JOIN subscription
|
||||
ON (actor_profile.id = subscription.sender_id)
|
||||
WHERE
|
||||
subscription.recipient_id = $1
|
||||
AND ($2::integer IS NULL OR subscription.id < $2)
|
||||
ORDER BY subscription.id DESC
|
||||
LIMIT $3
|
||||
",
|
||||
&[&recipient_id, &max_subscription_id, &limit],
|
||||
).await?;
|
||||
let subscriptions = rows.iter()
|
||||
.map(Subscription::try_from)
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(subscriptions)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use postgres_types::FromSql;
|
||||
use tokio_postgres::Row;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
|
||||
#[derive(FromSql)]
|
||||
#[postgres(name = "subscription")]
|
||||
pub struct DbSubscription {
|
||||
|
@ -12,3 +18,24 @@ pub struct DbSubscription {
|
|||
pub expires_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub struct Subscription {
|
||||
pub id: i32,
|
||||
pub sender: DbActorProfile,
|
||||
pub sender_address: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&Row> for Subscription {
|
||||
|
||||
type Error = DatabaseError;
|
||||
|
||||
fn try_from(row: &Row) -> Result<Self, Self::Error> {
|
||||
let db_subscription: DbSubscription = row.try_get("subscription")?;
|
||||
let db_sender: DbActorProfile = row.try_get("sender")?;
|
||||
Ok(Self {
|
||||
id: db_subscription.id,
|
||||
sender: db_sender,
|
||||
sender_address: db_subscription.sender_address,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue