mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 12:01:08 +00:00
add create article endpoint
This commit is contained in:
parent
b69b0fd30a
commit
59c6c8d46a
7 changed files with 87 additions and 36 deletions
48
src/api.rs
48
src/api.rs
|
@ -1,32 +1,68 @@
|
|||
use crate::database::DatabaseHandle;
|
||||
|
||||
use crate::error::MyResult;
|
||||
use crate::federation::objects::article::DbArticle;
|
||||
use crate::federation::objects::instance::DbInstance;
|
||||
use crate::utils::generate_object_id;
|
||||
use activitypub_federation::config::Data;
|
||||
use activitypub_federation::fetch::object_id::ObjectId;
|
||||
use axum::extract::{Path, Query};
|
||||
use anyhow::anyhow;
|
||||
use axum::extract::Query;
|
||||
use axum::routing::{get, post};
|
||||
use axum::{Form, Json, Router};
|
||||
use axum_macros::debug_handler;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use url::Url;
|
||||
|
||||
pub fn api_routes() -> Router {
|
||||
Router::new()
|
||||
.route("/article/:title", get(get_article))
|
||||
.route("/article", get(get_article).post(create_article))
|
||||
.route("/resolve_object", get(resolve_object))
|
||||
.route("/instance", get(get_local_instance))
|
||||
.route("/instance/follow", post(follow_instance))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CreateArticle {
|
||||
pub title: String,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
async fn create_article(
|
||||
data: Data<DatabaseHandle>,
|
||||
Form(create_article): Form<CreateArticle>,
|
||||
) -> MyResult<Json<DbArticle>> {
|
||||
let local_instance_id = data.local_instance().ap_id;
|
||||
let ap_id = generate_object_id(local_instance_id.inner())?.into();
|
||||
let article = DbArticle {
|
||||
title: create_article.title,
|
||||
text: create_article.text,
|
||||
ap_id,
|
||||
instance: local_instance_id,
|
||||
local: true,
|
||||
};
|
||||
let mut articles = data.articles.lock().unwrap();
|
||||
articles.push(article.clone());
|
||||
Ok(Json(article))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct GetArticle {
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
async fn get_article(
|
||||
Path(title): Path<String>,
|
||||
Query(query): Query<GetArticle>,
|
||||
data: Data<DatabaseHandle>,
|
||||
) -> MyResult<Json<DbArticle>> {
|
||||
let instance = data.local_instance();
|
||||
let article = DbArticle::new(title, "dummy".to_string(), instance.ap_id)?;
|
||||
let articles = data.articles.lock().unwrap();
|
||||
let article = articles
|
||||
.iter()
|
||||
.find(|a| a.title == query.title)
|
||||
.ok_or(anyhow!("not found"))?
|
||||
.clone();
|
||||
Ok(Json(article))
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ pub type DatabaseHandle = Arc<Database>;
|
|||
pub struct Database {
|
||||
pub instances: Mutex<Vec<DbInstance>>,
|
||||
pub users: Mutex<Vec<DbUser>>,
|
||||
pub posts: Mutex<Vec<DbArticle>>,
|
||||
pub articles: Mutex<Vec<DbArticle>>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
|
|
|
@ -30,7 +30,7 @@ pub async fn federation_config(hostname: &str) -> Result<FederationConfig<Databa
|
|||
let database = Arc::new(Database {
|
||||
instances: Mutex::new(vec![local_instance]),
|
||||
users: Mutex::new(vec![]),
|
||||
posts: Mutex::new(vec![]),
|
||||
articles: Mutex::new(vec![]),
|
||||
});
|
||||
let config = FederationConfig::builder()
|
||||
.domain(hostname)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::federation::objects::instance::DbInstance;
|
||||
use crate::{database::DatabaseHandle, error::Error, generate_object_id};
|
||||
use crate::{database::DatabaseHandle, error::Error};
|
||||
use activitypub_federation::kinds::object::ArticleType;
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
|
@ -20,23 +20,6 @@ pub struct DbArticle {
|
|||
pub local: bool,
|
||||
}
|
||||
|
||||
impl DbArticle {
|
||||
pub fn new(
|
||||
title: String,
|
||||
text: String,
|
||||
attributed_to: ObjectId<DbInstance>,
|
||||
) -> Result<DbArticle, Error> {
|
||||
let ap_id = generate_object_id(attributed_to.inner())?.into();
|
||||
Ok(DbArticle {
|
||||
title,
|
||||
text,
|
||||
ap_id,
|
||||
instance: attributed_to,
|
||||
local: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Article {
|
||||
|
@ -60,7 +43,7 @@ impl Object for DbArticle {
|
|||
object_id: Url,
|
||||
data: &Data<Self::DataType>,
|
||||
) -> Result<Option<Self>, Self::Error> {
|
||||
let posts = data.posts.lock().unwrap();
|
||||
let posts = data.articles.lock().unwrap();
|
||||
let res = posts
|
||||
.clone()
|
||||
.into_iter()
|
||||
|
@ -98,7 +81,7 @@ impl Object for DbArticle {
|
|||
local: false,
|
||||
};
|
||||
|
||||
let mut lock = data.posts.lock().unwrap();
|
||||
let mut lock = data.articles.lock().unwrap();
|
||||
lock.push(post.clone());
|
||||
Ok(post)
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ impl DbInstance {
|
|||
data: &Data<DatabaseHandle>,
|
||||
) -> Result<(), Error> {
|
||||
let follow = Follow::new(self.ap_id.clone(), other.ap_id.clone())?;
|
||||
dbg!(&follow);
|
||||
self.send(follow, vec![other.shared_inbox_or_inbox()], data)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
|
|
@ -47,7 +47,6 @@ pub async fn http_post_inbox(
|
|||
data: Data<DatabaseHandle>,
|
||||
activity_data: ActivityData,
|
||||
) -> impl IntoResponse {
|
||||
dbg!("receive activity");
|
||||
receive_activity::<WithContext<InboxActivities>, DbInstance, DatabaseHandle>(
|
||||
activity_data,
|
||||
&data,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
extern crate fediwiki;
|
||||
|
||||
use fediwiki::api::{FollowInstance, ResolveObject};
|
||||
use fediwiki::api::{CreateArticle, FollowInstance, GetArticle, ResolveObject};
|
||||
use fediwiki::error::MyResult;
|
||||
use fediwiki::federation::objects::article::DbArticle;
|
||||
use fediwiki::federation::objects::instance::DbInstance;
|
||||
|
@ -28,17 +28,37 @@ static CLIENT: Lazy<Client> = Lazy::new(|| Client::new());
|
|||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_get_article() -> MyResult<()> {
|
||||
async fn test_create_and_read_article() -> MyResult<()> {
|
||||
setup();
|
||||
let hostname = "localhost:8131";
|
||||
let handle = tokio::task::spawn(async {
|
||||
start(hostname).await.unwrap();
|
||||
});
|
||||
|
||||
let title = "Manu_Chao";
|
||||
let res: DbArticle = get(hostname, &format!("article/{title}")).await?;
|
||||
assert_eq!(title, res.title);
|
||||
assert!(res.local);
|
||||
// error on nonexistent article
|
||||
let get_article = GetArticle {
|
||||
title: "Manu_Chao".to_string(),
|
||||
};
|
||||
let not_found =
|
||||
get_query::<DbArticle, _>(hostname, &format!("article"), Some(get_article.clone())).await;
|
||||
assert!(not_found.is_err());
|
||||
|
||||
// create article
|
||||
let create_article = CreateArticle {
|
||||
title: get_article.title.to_string(),
|
||||
text: "Lorem ipsum".to_string(),
|
||||
};
|
||||
let create_res: DbArticle = post(hostname, "article", &create_article).await?;
|
||||
assert_eq!(create_article.title, create_res.title);
|
||||
assert!(create_res.local);
|
||||
|
||||
// now article can be read
|
||||
let get_res: DbArticle =
|
||||
get_query(hostname, &format!("article"), Some(get_article.clone())).await?;
|
||||
assert_eq!(create_article.title, get_res.title);
|
||||
assert_eq!(create_article.text, get_res.text);
|
||||
assert!(get_res.local);
|
||||
|
||||
handle.abort();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -73,6 +93,7 @@ async fn test_follow_instance() -> MyResult<()> {
|
|||
let follow_instance = FollowInstance {
|
||||
instance_id: beta_instance_resolved.ap_id,
|
||||
};
|
||||
// cant use post helper because follow doesnt return json
|
||||
CLIENT
|
||||
.post(format!("http://{hostname_alpha}/api/v1/instance/follow"))
|
||||
.form(&follow_instance)
|
||||
|
@ -110,3 +131,16 @@ where
|
|||
let alpha_instance: T = res.send().await?.json().await?;
|
||||
Ok(alpha_instance)
|
||||
}
|
||||
|
||||
async fn post<T: Serialize, R>(hostname: &str, endpoint: &str, form: &T) -> MyResult<R>
|
||||
where
|
||||
R: for<'de> Deserialize<'de>,
|
||||
{
|
||||
Ok(CLIENT
|
||||
.post(format!("http://{}/api/v1/{}", hostname, endpoint))
|
||||
.form(form)
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue