add basic api call with test

This commit is contained in:
Felix Ableitner 2023-11-15 13:05:07 +01:00
parent df9da3784b
commit 1098ecd4a1
8 changed files with 108 additions and 48 deletions

1
Cargo.lock generated
View File

@ -464,6 +464,7 @@ dependencies = [
"enum_delegate", "enum_delegate",
"env_logger", "env_logger",
"rand", "rand",
"reqwest",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",

View File

@ -18,3 +18,6 @@ serde_json = "1.0.108"
tokio = { version = "1.34.0", features = ["full"] } tokio = { version = "1.34.0", features = ["full"] }
tracing = "0.1.40" tracing = "0.1.40"
url = "2.4.1" url = "2.4.1"
[dev-dependencies]
reqwest = "0.11.22"

13
src/api.rs Normal file
View File

@ -0,0 +1,13 @@
use crate::error::MyResult;
use crate::federation::objects::article::DbArticle;
use crate::federation::objects::instance::DbInstance;
use axum::extract::Path;
use axum::Json;
use axum_macros::debug_handler;
#[debug_handler]
pub async fn api_get_article(Path(title): Path<String>) -> MyResult<Json<DbArticle>> {
let instance = DbInstance::new("localhost")?;
let article = DbArticle::new(title, "dummy".to_string(), instance.ap_id)?;
Ok(Json(article))
}

View File

@ -11,8 +11,9 @@ use activitypub_federation::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DbArticle { pub struct DbArticle {
pub title: String,
pub text: String, pub text: String,
pub ap_id: ObjectId<DbArticle>, pub ap_id: ObjectId<DbArticle>,
pub instance: ObjectId<DbInstance>, pub instance: ObjectId<DbInstance>,
@ -20,9 +21,14 @@ pub struct DbArticle {
} }
impl DbArticle { impl DbArticle {
pub fn new(text: String, attributed_to: ObjectId<DbInstance>) -> Result<DbArticle, Error> { pub fn new(
title: String,
text: String,
attributed_to: ObjectId<DbInstance>,
) -> Result<DbArticle, Error> {
let ap_id = generate_object_id(attributed_to.inner())?.into(); let ap_id = generate_object_id(attributed_to.inner())?.into();
Ok(DbArticle { Ok(DbArticle {
title,
text, text,
ap_id, ap_id,
instance: attributed_to, instance: attributed_to,
@ -41,6 +47,7 @@ pub struct Article {
#[serde(deserialize_with = "deserialize_one_or_many")] #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
content: String, content: String,
name: String,
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -69,6 +76,7 @@ impl Object for DbArticle {
attributed_to: self.instance, attributed_to: self.instance,
to: vec![public(), instance.followers_url()?], to: vec![public(), instance.followers_url()?],
content: self.text, content: self.text,
name: self.title,
}) })
} }
@ -83,6 +91,7 @@ impl Object for DbArticle {
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> { async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
let post = DbArticle { let post = DbArticle {
title: json.name,
text: json.content, text: json.content,
ap_id: json.id, ap_id: json.id,
instance: json.attributed_to, instance: json.attributed_to,

View File

@ -1,5 +1,5 @@
use crate::database::DatabaseHandle; use crate::database::DatabaseHandle;
use crate::error::Error; use crate::error::MyResult;
use crate::federation::objects::person::{DbUser, Person, PersonAcceptedActivities}; use crate::federation::objects::person::{DbUser, Person, PersonAcceptedActivities};
use activitypub_federation::axum::inbox::{receive_activity, ActivityData}; use activitypub_federation::axum::inbox::{receive_activity, ActivityData};
use activitypub_federation::axum::json::FederationJson; use activitypub_federation::axum::json::FederationJson;
@ -14,7 +14,7 @@ use axum_macros::debug_handler;
pub async fn http_get_user( pub async fn http_get_user(
Path(name): Path<String>, Path(name): Path<String>,
data: Data<DatabaseHandle>, data: Data<DatabaseHandle>,
) -> Result<FederationJson<WithContext<Person>>, Error> { ) -> MyResult<FederationJson<WithContext<Person>>> {
let db_user = data.read_user(&name)?; let db_user = data.read_user(&name)?;
let json_user = db_user.into_json(&data).await?; let json_user = db_user.into_json(&data).await?;
Ok(FederationJson(WithContext::new_default(json_user))) Ok(FederationJson(WithContext::new_default(json_user)))

52
src/lib.rs Normal file
View File

@ -0,0 +1,52 @@
use crate::utils::generate_object_id;
use tracing::log::LevelFilter;
use activitypub_federation::config::FederationMiddleware;
use axum::{
routing::{get, post},
Router, Server,
};
use crate::api::api_get_article;
use crate::error::MyResult;
use crate::federation::routes::http_get_user;
use crate::federation::routes::http_post_user_inbox;
use federation::federation_config;
use std::net::ToSocketAddrs;
use tracing::info;
mod api;
mod database;
pub mod error;
pub mod federation;
mod utils;
pub async fn start(hostname: &str) -> MyResult<()> {
env_logger::builder()
.filter_level(LevelFilter::Warn)
.filter_module("activitypub_federation", LevelFilter::Info)
.filter_module("fediwiki", LevelFilter::Info)
.init();
let config = federation_config(hostname).await?;
info!("Listening with axum on {hostname}");
let config = config.clone();
let app = Router::new()
// federation routes
.route("/:user/inbox", post(http_post_user_inbox))
.route("/:user", get(http_get_user))
// api routes
.route("/api/v1/article/:title", get(api_get_article))
.layer(FederationMiddleware::new(config));
let addr = hostname
.to_socket_addrs()?
.next()
.expect("Failed to lookup domain name");
let server = Server::bind(&addr).serve(app.into_make_service());
tokio::spawn(server);
Ok(())
}

View File

@ -1,48 +1,8 @@
use crate::utils::generate_object_id; use fediwiki::error::MyResult;
use error::Error; use fediwiki::start;
use tracing::log::LevelFilter;
use activitypub_federation::config::FederationMiddleware;
use axum::{
routing::{get, post},
Router, Server,
};
use crate::federation::routes::http_get_user;
use crate::federation::routes::http_post_user_inbox;
use federation::federation_config;
use std::net::ToSocketAddrs;
use tracing::info;
mod database;
mod error;
mod federation;
mod utils;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Error> { pub async fn main() -> MyResult<()> {
env_logger::builder() start("localhost:8131").await?;
.filter_level(LevelFilter::Warn)
.filter_module("activitypub_federation", LevelFilter::Info)
.filter_module("fediwiki", LevelFilter::Info)
.init();
let config = federation_config("localhost:8001").await?;
let hostname = config.domain();
info!("Listening with axum on {hostname}");
let config = config.clone();
let app = Router::new()
.route("/:user/inbox", post(http_post_user_inbox))
.route("/:user", get(http_get_user))
.layer(FederationMiddleware::new(config));
let addr = hostname
.to_socket_addrs()?
.next()
.expect("Failed to lookup domain name");
let server = Server::bind(&addr).serve(app.into_make_service());
tokio::spawn(server);
Ok(()) Ok(())
} }

22
tests/test.rs Normal file
View File

@ -0,0 +1,22 @@
extern crate fediwiki;
use fediwiki::federation::objects::article::DbArticle;
use fediwiki::start;
#[tokio::test]
async fn test_get_article() {
let hostname = "localhost:8131";
let join = tokio::task::spawn(async {
start(hostname).await.unwrap();
});
let title = "Manu_Chao";
let res: DbArticle = reqwest::get(format!("http://{hostname}/api/v1/article/{title}"))
.await
.unwrap()
.json()
.await
.unwrap();
assert_eq!(title, res.title);
assert!(res.local);
join.abort();
}