Merge pull request #245 from Plume-org/rest-api

Some API endpoints for articles
This commit is contained in:
Baptiste Gelez 2018-09-29 16:33:31 +01:00 committed by GitHub
commit 236cf14406
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 244 additions and 2 deletions

53
Cargo.lock generated
View file

@ -236,6 +236,14 @@ dependencies = [
"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]]
name = "cc"
version = "1.0.24"
@ -604,6 +612,11 @@ dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fnv"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -931,6 +944,11 @@ dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.2"
@ -1451,6 +1469,7 @@ version = "0.2.0"
dependencies = [
"activitypub 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"atom_syndication 0.6.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)",
"colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1460,6 +1479,7 @@ dependencies = [
"guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"multipart 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
"plume-api 0.1.0",
"plume-common 0.2.0",
"plume-models 0.2.0",
"rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=55459db7732b9a240826a5c120c650f87e3372ce)",
@ -1471,6 +1491,7 @@ dependencies = [
"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)",
"serde_json 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_qs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tera 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)",
"validator 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"validator_derive 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1478,6 +1499,15 @@ dependencies = [
"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]]
name = "plume-common"
version = "0.2.0"
@ -1510,11 +1540,13 @@ dependencies = [
"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)",
"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)",
"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)",
"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)",
"plume-api 0.1.0",
"plume-common 0.2.0",
"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)",
@ -1993,6 +2025,21 @@ dependencies = [
"serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_qs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 1.0.1 (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]]
name = "serde_urlencoded"
version = "0.5.3"
@ -2745,6 +2792,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 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 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 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"
@ -2789,6 +2837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
"checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426"
"checksum filetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da4b9849e77b13195302c174324b5ba73eec9b236b24c221a61000daefb95c5f"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05"
@ -2824,6 +2873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2"
"checksum isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e31a8281fc93ec9693494da65fbf28c0c2aa60a2eaec25dc58e2f31952e95edc"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
@ -2933,6 +2983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum serde 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e67977d7523ce4d9284ed58918af99392de8edb6192c44afefcf634654ab7f"
"checksum serde_derive 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)" = "5569c52faae3e21b9abae2cc5cfbb56ed008bfcac480ad62bc241b828f0b0aee"
"checksum serde_json 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "59790990c5115d16027f00913e2e66de23a51f70422e549d2ad68c8c5f268f1c"
"checksum serde_qs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e5873a8c1e2dc41f1d9305b7510f70768376dc974f81c0b9ce6b4d5b48be0203"
"checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650"
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"

View file

@ -5,6 +5,7 @@ version = "0.2.0"
[dependencies]
activitypub = "0.1.3"
atom_syndication = "0.6"
canapi = "0.1"
colored = "1.6"
dotenv = "0.13"
failure = "0.1"
@ -16,6 +17,7 @@ rpassword = "2.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
serde_qs = "0.4"
tera = "0.11"
validator = "0.7"
validator_derive = "0.7"
@ -30,6 +32,9 @@ version = "0.4"
features = ["postgres", "r2d2", "chrono"]
version = "*"
[dependencies.plume-api]
path = "plume-api"
[dependencies.plume-common]
path = "plume-common"
@ -58,4 +63,4 @@ git = "https://github.com/BaptisteGelez/rocket_i18n"
rev = "75a3bfd7b847324c078a355a7f101f8241a9f59b"
[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"
ammonia = "1.2.0"
bcrypt = "0.2"
canapi = "0.1"
heck = "0.3.0"
lazy_static = "*"
openssl = "0.10.11"
@ -25,6 +26,9 @@ version = "0.4"
features = ["postgres", "r2d2", "chrono"]
version = "1.3.2"
[dependencies.plume-api]
path = "../plume-api"
[dependencies.plume-common]
path = "../plume-common"

View file

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

View file

@ -3,11 +3,13 @@ use activitypub::{
link,
object::{Article, Tombstone}
};
use canapi::{Error, Provider};
use chrono::{NaiveDateTime, TimeZone, Utc};
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any};
use heck::KebabCase;
use serde_json;
use plume_api::posts::PostEndpoint;
use plume_common::activity_pub::{
Hashtag, Source,
PUBLIC_VISIBILTY, Id, IntoId,
@ -55,6 +57,54 @@ pub struct NewPost {
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 query = posts::table.into_boxed();
if let Some(title) = filter.title {
query = query.filter(posts::title.eq(title));
}
if let Some(subtitle) = filter.subtitle {
query = query.filter(posts::subtitle.eq(subtitle));
}
if let Some(content) = filter.content {
query = query.filter(posts::content.eq(content));
}
query.get_results::<Post>(conn).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> {
unimplemented!()
}
fn update(_conn: &PgConnection, _id: i32, _new_data: PostEndpoint) -> Result<PostEndpoint, Error> {
unimplemented!()
}
fn delete(conn: &PgConnection, id: i32) {
Post::get(conn, id).map(|p| p.delete(conn));
}
}
impl Post {
insert!(posts, NewPost);
get!(posts);

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

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

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

@ -0,0 +1,23 @@
use canapi::Provider;
use diesel::PgConnection;
use rocket::http::uri::Origin;
use rocket_contrib::Json;
use serde_json;
use serde_qs;
use plume_api::posts::PostEndpoint;
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 as Provider<PgConnection>>::get(&*conn, id).ok();
Json(json!(post))
}
#[get("/posts")]
fn list(conn: DbConn, uri: &Origin) -> Json<serde_json::Value> {
let query: PostEndpoint = serde_qs::from_str(uri.query().unwrap_or("")).expect("Invalid query string");
let post = <Post as Provider<PgConnection>>::list(&*conn, query);
Json(json!(post))
}

View file

@ -3,6 +3,7 @@
extern crate activitypub;
extern crate atom_syndication;
extern crate canapi;
extern crate chrono;
extern crate colored;
extern crate diesel;
@ -12,6 +13,7 @@ extern crate gettextrs;
extern crate guid_create;
extern crate heck;
extern crate multipart;
extern crate plume_api;
extern crate plume_common;
extern crate plume_models;
#[macro_use]
@ -25,6 +27,7 @@ extern crate serde;
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate serde_qs;
extern crate validator;
#[macro_use]
extern crate validator_derive;
@ -36,6 +39,7 @@ use rocket_contrib::Template;
use rocket_csrf::CsrfFairingBuilder;
use workerpool::{Pool, thunk::ThunkWorker};
mod api;
mod inbox;
mod setup;
mod routes;
@ -142,6 +146,10 @@ fn main() {
routes::errors::csrf_violation
])
.mount("/api/v1", routes![
api::posts::get,
api::posts::list,
])
.catch(catchers![
routes::errors::not_found,
routes::errors::server_error