Add canapi and try to use for the API

This commit is contained in:
Bat 2018-09-19 15:49:34 +01:00
parent eb24ba1774
commit 1500267125
16 changed files with 211 additions and 10 deletions

22
Cargo.lock generated
View file

@ -236,6 +236,14 @@ dependencies = [
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "canapi"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.24" version = "1.0.24"
@ -1478,6 +1486,15 @@ dependencies = [
"workerpool 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "workerpool 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "plume-api"
version = "0.1.0"
dependencies = [
"canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "plume-common" name = "plume-common"
version = "0.2.0" version = "0.2.0"
@ -1510,11 +1527,13 @@ dependencies = [
"activitypub 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "activitypub 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)",
"plume-api 0.1.0",
"plume-common 0.2.0", "plume-common 0.2.0",
"reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=55459db7732b9a240826a5c120c650f87e3372ce)", "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=55459db7732b9a240826a5c120c650f87e3372ce)",
@ -2745,6 +2764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" "checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781"
"checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62" "checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62"
"checksum canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3e02a04f44b531d851d2db62f95aabf65d033a6724767a4bed9732563e9bc4"
"checksum cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "70f2a88c2e69ceee91c209d8ef25b81fc1a65f42c7f14dfd59d1fed189e514d1" "checksum cc 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "70f2a88c2e69ceee91c209d8ef25b81fc1a65f42c7f14dfd59d1fed189e514d1"
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
"checksum chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f74ad218e66339b11fd23f693fb8f1d621e80ba6ac218297be26073365d163d" "checksum chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f74ad218e66339b11fd23f693fb8f1d621e80ba6ac218297be26073365d163d"

View file

@ -58,4 +58,4 @@ git = "https://github.com/BaptisteGelez/rocket_i18n"
rev = "75a3bfd7b847324c078a355a7f101f8241a9f59b" rev = "75a3bfd7b847324c078a355a7f101f8241a9f59b"
[workspace] [workspace]
members = ["plume-models", "plume-common"] members = ["plume-api", "plume-models", "plume-common"]

15
docs/API.md Normal file
View file

@ -0,0 +1,15 @@
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<div id="api"></div>
<script>
const ui = SwaggerUIBundle({
url: "/Plume/api.yaml",
dom_id: '#api',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "StandaloneLayout"
})
</script>

45
docs/api.yaml Normal file
View file

@ -0,0 +1,45 @@
openapi: "3.0"
info:
version: "1.0.0"
title: "Plume REST API"
servers:
- url: http://localhost:7878/api/v1
description: Your local instance
- url: https://baptiste.gelez.xyz/api/v1
description: Demo instance
paths:
/posts/{id}:
get:
description:
Retrieves a post by its ID.
responses:
'200':
The post was found
'403':
The post exists, but you don't have the rights to fetch it (it is probably a private draft)
'404':
The post was not found
/posts/:
get:
description:
List posts.
definitions:
Post:
type: "object"
properties:
title:
type: "string"
example: "Hello, world!"
id:
type: "integer"
format: "int64"
example: 42
subtitle:
type: "string"
example: "My first post."
content:
type: "string"
format: "<p>This is my first post. Thanks for reading.</p>"

9
plume-api/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "plume-api"
version = "0.1.0"
authors = ["Bat' <baptiste@gelez.xyz>"]
[dependencies]
canapi = "0.1"
serde = "1.0"
serde_derive = "1.0"

18
plume-api/src/lib.rs Normal file
View file

@ -0,0 +1,18 @@
extern crate canapi;
extern crate serde;
#[macro_use]
extern crate serde_derive;
macro_rules! api {
($url:expr => $ep:ty) => {
impl Endpoint for $ep {
type Id = i32;
fn endpoint() -> &'static str {
$url
}
}
};
}
pub mod posts;

11
plume-api/src/posts.rs Normal file
View file

@ -0,0 +1,11 @@
use canapi::Endpoint;
#[derive(Default, Serialize, Deserialize)]
pub struct PostEndpoint {
pub id: Option<i32>,
pub title: Option<String>,
pub subtitle: Option<String>,
pub content: Option<String>
}
api!("/api/v1/posts" => PostEndpoint);

View file

@ -7,6 +7,7 @@ authors = ["Baptiste Gelez <baptiste@gelez.xyz>"]
activitypub = "0.1.1" activitypub = "0.1.1"
ammonia = "1.2.0" ammonia = "1.2.0"
bcrypt = "0.2" bcrypt = "0.2"
canapi = "0.1"
heck = "0.3.0" heck = "0.3.0"
lazy_static = "*" lazy_static = "*"
openssl = "0.10.11" openssl = "0.10.11"
@ -25,6 +26,9 @@ version = "0.4"
features = ["postgres", "r2d2", "chrono"] features = ["postgres", "r2d2", "chrono"]
version = "1.3.2" version = "1.3.2"
[dependencies.plume-api]
path = "../plume-api"
[dependencies.plume-common] [dependencies.plume-common]
path = "../plume-common" path = "../plume-common"

View file

@ -3,6 +3,7 @@ use activitypub::{
link, link,
object::{Note} object::{Note}
}; };
use canapi::Provider;
use chrono; use chrono;
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, dsl::any}; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, dsl::any};
use serde_json; use serde_json;

View file

@ -3,6 +3,7 @@
extern crate activitypub; extern crate activitypub;
extern crate ammonia; extern crate ammonia;
extern crate bcrypt; extern crate bcrypt;
extern crate canapi;
extern crate chrono; extern crate chrono;
#[macro_use] #[macro_use]
extern crate diesel; extern crate diesel;
@ -10,6 +11,7 @@ extern crate heck;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate openssl; extern crate openssl;
extern crate plume_api;
extern crate plume_common; extern crate plume_common;
extern crate reqwest; extern crate reqwest;
extern crate rocket; extern crate rocket;

View file

@ -1,4 +1,5 @@
use activitypub::activity; use activitypub::activity;
use canapi::Provider;
use chrono; use chrono;
use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods};

View file

@ -3,11 +3,13 @@ use activitypub::{
link, link,
object::{Article, Tombstone} object::{Article, Tombstone}
}; };
use canapi::{Error, Provider};
use chrono::{NaiveDateTime, TimeZone, Utc}; use chrono::{NaiveDateTime, TimeZone, Utc};
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any}; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any, Expression, BoolExpressionMethods};
use heck::KebabCase; use heck::KebabCase;
use serde_json; use serde_json;
use plume_api::posts::PostEndpoint;
use plume_common::activity_pub::{ use plume_common::activity_pub::{
Hashtag, Source, Hashtag, Source,
PUBLIC_VISIBILTY, Id, IntoId, PUBLIC_VISIBILTY, Id, IntoId,
@ -55,6 +57,56 @@ pub struct NewPost {
pub source: String, pub source: String,
} }
impl Provider<PgConnection> for Post {
type Data = PostEndpoint;
fn get(conn: &PgConnection, id: i32) -> Result<PostEndpoint, Error> {
Post::get(conn, id).map(|p| Ok(PostEndpoint {
id: Some(p.id),
title: Some(p.title.clone()),
subtitle: Some(p.subtitle.clone()),
content: Some(p.content.get().clone())
})).unwrap_or(Err(Error::NotFound("Get Post".to_string())))
}
fn list(conn: &PgConnection, filter: PostEndpoint) -> Vec<PostEndpoint> {
let mut filters = Vec::new();
if let Some(title) = filter.title {
filters.push(posts::title.eq(title));
}
let filters = filters.into_iter();
let res = if let Some(first_filter) = filters.next() {
posts::table.filter(filters.fold(first_filter, |q, f| q.and(f)))
.get_results::<Post>(conn)
} else {
posts::table.get_results::<Post>(conn)
};
res.map(|ps| ps.into_iter()
.map(|p| PostEndpoint {
id: Some(p.id),
title: Some(p.title.clone()),
subtitle: Some(p.subtitle.clone()),
content: Some(p.content.get().clone())
})
.collect()
).unwrap_or(vec![])
}
fn create(conn: &PgConnection, query: PostEndpoint) -> Result<PostEndpoint, Error> {
}
fn update(conn: &PgConnection, id: i32, new_data: PostEndpoint) -> Result<PostEndpoint, Error> {
}
fn delete(conn: &PgConnection, id: i32) {
Post::get(conn, id).map(|p| p.delete(conn));
}
}
impl Post { impl Post {
insert!(posts, NewPost); insert!(posts, NewPost);
get!(posts); get!(posts);

View file

@ -325,7 +325,8 @@ msgstr[1] "{{ count }} autoras en este blog: "
msgid "Login or use your Fediverse account to interact with this article" msgid "Login or use your Fediverse account to interact with this article"
msgstr "" msgstr ""
"Conéctese ou utilice a súa conta no fediverso para interactuar con este artigo" "Conéctese ou utilice a súa conta no fediverso para interactuar con este "
"artigo"
msgid "Optional" msgid "Optional"
msgstr "Opcional" msgstr "Opcional"
@ -486,7 +487,6 @@ msgstr "Descrición"
msgid "Content warning" msgid "Content warning"
msgstr "Aviso sobre o contido" msgstr "Aviso sobre o contido"
msgid "File" msgid "File"
msgstr "Ficheiro" msgstr "Ficheiro"
@ -496,7 +496,8 @@ msgstr "Enviar"
msgid "" msgid ""
"Sorry, but registrations are closed on this instance. Try to find another one" "Sorry, but registrations are closed on this instance. Try to find another one"
msgstr "" msgstr ""
"Lamentámolo, pero o rexistro está pechado en esta instancia. Intente atopar outra" "Lamentámolo, pero o rexistro está pechado en esta instancia. Intente atopar "
"outra"
msgid "Subtitle" msgid "Subtitle"
msgstr "Subtítulo" msgstr "Subtítulo"
@ -553,9 +554,10 @@ msgid ""
"Something is wrong with your CSRF token. Make sure cookies are enabled in " "Something is wrong with your CSRF token. Make sure cookies are enabled in "
"you browser, and try reloading this page. If you continue to see this error " "you browser, and try reloading this page. If you continue to see this error "
"message, please report it." "message, please report it."
msgstr "Hai un problema co seu testemuño CSRF. Asegúrese de ter as cookies activadas " msgstr ""
"no navegador, e recargue a páxina. Si persiste o aviso de este fallo, " "Hai un problema co seu testemuño CSRF. Asegúrese de ter as cookies activadas "
" informe por favor." "no navegador, e recargue a páxina. Si persiste o aviso de este fallo, "
"informe por favor."
msgid "Administration of {{ instance.name }}" msgid "Administration of {{ instance.name }}"
msgstr "Administración de {{ instance_name }}" msgstr "Administración de {{ instance_name }}"
@ -600,7 +602,12 @@ msgid "Delete your account"
msgstr "Eliminar a súa conta" msgstr "Eliminar a súa conta"
msgid "Sorry, but as an admin, you can't leave your instance." msgid "Sorry, but as an admin, you can't leave your instance."
msgstr "Lamentámolo, pero como administradora, non pode deixar a súa instancia." msgstr ""
"Lamentámolo, pero como administradora, non pode deixar a súa instancia."
msgid "Users" msgid "Users"
msgstr "Usuarias" msgstr "Usuarias"
#, fuzzy
msgid "This post isn't published yet."
msgstr "Esto é un borrador, non publicar por agora."

1
src/api/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod posts;

11
src/api/posts.rs Normal file
View file

@ -0,0 +1,11 @@
use rocket_contrib::Json;
use serde_json;
use plume_models::db_conn::DbConn;
use plume_models::posts::Post;
#[get("/posts/<id>")]
fn get(id: i32, conn: DbConn) -> Json<serde_json::Value> {
let post = Post::get(&*conn, id).unwrap();
Json(post.to_json(&*conn))
}

View file

@ -36,6 +36,7 @@ use rocket_contrib::Template;
use rocket_csrf::CsrfFairingBuilder; use rocket_csrf::CsrfFairingBuilder;
use workerpool::{Pool, thunk::ThunkWorker}; use workerpool::{Pool, thunk::ThunkWorker};
mod api;
mod inbox; mod inbox;
mod setup; mod setup;
mod routes; mod routes;
@ -142,6 +143,9 @@ fn main() {
routes::errors::csrf_violation routes::errors::csrf_violation
]) ])
.mount("/api/v1", routes![
api::posts::get
])
.catch(catchers![ .catch(catchers![
routes::errors::not_found, routes::errors::not_found,
routes::errors::server_error routes::errors::server_error