containers-index #6

Merged
JakeHillion merged 2 commits from containers-index into main 2022-07-25 13:55:59 +01:00
11 changed files with 298 additions and 27 deletions

111
Cargo.lock generated
View File

@ -514,23 +514,26 @@ dependencies = [
[[package]]
name = "diesel"
version = "1.4.8"
version = "2.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
checksum = "876a12f2d98c35d1dfaf74083664be5bb71606b72f4756f3792cdb1ddb192b46"
dependencies = [
"bitflags",
"byteorder",
"diesel_derives",
"itoa",
"pq-sys",
"r2d2",
"uuid",
]
[[package]]
name = "diesel_derives"
version = "1.4.1"
version = "2.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
checksum = "2497d9cefebedc40492310f3c94493e1d0bd5b11b5e1bfc0a7f4b106012203ea"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
@ -538,10 +541,11 @@ dependencies = [
[[package]]
name = "diesel_migrations"
version = "1.4.0"
version = "2.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c"
checksum = "af3e284b00439e362c3f55849a103f38b4c5a9918ae3cf34fca2268b6eb52fef"
dependencies = [
"diesel",
"migrations_internals",
"migrations_macros",
]
@ -632,6 +636,21 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
@ -639,6 +658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -647,6 +667,17 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
@ -679,6 +710,12 @@ dependencies = [
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
[[package]]
name = "futures-task"
version = "0.3.21"
@ -691,9 +728,13 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite 0.2.9",
"pin-utils",
"slab",
@ -863,12 +904,16 @@ name = "inventory-system"
version = "0.1.0"
dependencies = [
"async-std",
"async-trait",
"diesel",
"diesel_migrations",
"futures",
"log",
"rust-embed",
"serde",
"thiserror",
"tide",
"uuid",
]
[[package]]
@ -942,23 +987,23 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "migrations_internals"
version = "1.4.1"
version = "2.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860"
checksum = "e03913f1a1c044aa53b37c8f34011599bbf98914092a0444081555e9ca35022c"
dependencies = [
"diesel",
"serde",
"toml",
]
[[package]]
name = "migrations_macros"
version = "1.4.2"
version = "2.0.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c"
checksum = "9ccfa634200e9de9fe6497f568d54951e933e9e0ddf61058e52e24bda896de48"
dependencies = [
"migrations_internals",
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -1114,6 +1159,30 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@ -1696,6 +1765,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "typenum"
version = "1.15.0"
@ -1746,6 +1824,15 @@ dependencies = [
"serde",
]
[[package]]
name = "uuid"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
dependencies = [
"serde",
]
[[package]]
name = "value-bag"
version = "1.0.0-alpha.9"

View File

@ -9,10 +9,15 @@ edition = "2021"
log = "0.4"
thiserror = "1"
futures = "0.3.21"
async-std = "1"
async-trait = "0.1.56"
serde = { version = "1.0", features = ["derive"] }
tide = "0.17.0-beta.1"
diesel = { version = "1.4", features = ["postgres", "r2d2"] }
diesel_migrations = "1.4"
diesel = { version = "2.0.0-rc.1", features = ["postgres", "r2d2", "uuid"] }
diesel_migrations = "2.0.0-rc.1"
uuid = { version = "1.1.2", features = ["serde"] }
rust-embed = { version = "6.2.0", features = ["interpolate-folder-path"]}

View File

@ -0,0 +1,3 @@
-- This file should undo anything in `up.sql`
drop table if exists containers;

View File

@ -0,0 +1,11 @@
-- Your SQL goes here
create table containers
(
id uuid default gen_random_uuid() not null
primary key,
parent uuid
constraint foreign_key_name
references containers,
name varchar not null
);

30
src/api/containers.rs Normal file
View File

@ -0,0 +1,30 @@
use super::{ApiEndpoint, ApiResult, ApiSuccess, State};
use crate::db;
use std::sync::Arc;
use diesel::Queryable;
use serde::Serialize;
use uuid::Uuid;
pub fn setup_routes(mut route: tide::Route<Arc<State>>) {
route.at("/").get(ApiEndpoint(index));
}
#[derive(Queryable, Serialize)]
pub struct Container {
pub id: Uuid,
pub parent: Option<Uuid>,
pub name: String,
}
async fn index(req: tide::Request<Arc<State>>) -> ApiResult<Vec<Container>> {
use db::schema::containers::dsl::*;
use diesel::{QueryDsl, RunQueryDsl};
let mut connection = req.state().db.get().unwrap();
let items: Vec<Container> = containers.limit(5).load::<Container>(&mut connection)?;
Ok(ApiSuccess::Ok(items))
}

118
src/api/error.rs Normal file
View File

@ -0,0 +1,118 @@
use log::error;
use async_trait::async_trait;
use diesel::result::Error as DieselError;
use futures::future::Future;
use serde::Serialize;
use thiserror::Error;
use tide::{Body, Response, StatusCode};
pub type ApiResult<T> = Result<ApiSuccess<T>, ApiError>;
#[derive(Serialize, Error, Debug)]
pub enum ApiSuccess<T> {
Ok(T),
}
#[derive(Error, Debug)]
pub enum ApiError {
#[error("not found")]
NotFound,
#[error("database error: {0}")]
Database(DieselError),
}
impl From<DieselError> for ApiError {
fn from(err: DieselError) -> Self {
match err {
DieselError::NotFound => Self::NotFound,
e => Self::Database(e),
}
}
}
pub(super) struct ApiEndpoint<E>(pub(super) E);
#[async_trait]
impl<E, S, F, T> tide::Endpoint<S> for ApiEndpoint<E>
where
E: Fn(tide::Request<S>) -> F + Send + Sync + 'static,
S: Clone + Send + Sync + 'static,
F: Future<Output = ApiResult<T>> + Send + Sync + 'static,
T: Serialize,
{
async fn call(&self, req: tide::Request<S>) -> tide::Result {
Ok(ApiResultProxy::<T>::from(self.0(req).await).into())
}
}
struct ApiResultProxy<T>(pub Result<ApiSuccess<T>, ApiError>);
impl<T> From<ApiResult<T>> for ApiResultProxy<T> {
fn from(res: ApiResult<T>) -> Self {
Self(res)
}
}
#[derive(Serialize)]
enum ApiResponse<'a, T> {
#[serde(rename = "content")]
Content(&'a T),
#[serde(rename = "err")]
Err(&'a str),
}
impl<T: Serialize> From<ApiResultProxy<T>> for Response {
fn from(res: ApiResultProxy<T>) -> Self {
match res.0 {
Ok(resp) => {
let (status_code, body) = resp.response();
Response::builder(status_code)
.body(
Body::from_json(&ApiResponse::Content(body))
.expect("json serialisation should succeed"),
)
.build()
}
Err(err) => {
let (status_code, body) = err.response();
Response::builder(status_code)
.body(
Body::from_json(&ApiResponse::<()>::Err(&body))
.expect("json serialisation should succeed"),
)
.build()
}
}
}
}
impl<T> ApiSuccess<T> {
fn response(&self) -> (StatusCode, &T) {
match self {
Self::Ok(b) => (StatusCode::Ok, b),
}
}
}
impl ApiError {
fn response(&self) -> (StatusCode, String) {
match self {
Self::NotFound => (StatusCode::NotFound, "requested object not found".into()),
e => {
error!("internal server error handling request: {}", e);
(
StatusCode::InternalServerError,
"internal server error".into(),
)
}
}
}
}

View File

@ -1,17 +1,23 @@
mod containers;
mod error;
use error::{ApiEndpoint, ApiResult, ApiSuccess};
use std::sync::Arc;
use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool};
pub struct State {
_db: Pool<ConnectionManager<PgConnection>>,
db: Pool<ConnectionManager<PgConnection>>,
}
pub fn serve(db: Pool<ConnectionManager<PgConnection>>) -> tide::Server<Arc<State>> {
let state = Arc::new(State { _db: db });
let state = Arc::new(State { db });
let mut app = tide::with_state(state);
app.at("/hello").get(|_| async { Ok("Hello, Jake!") });
containers::setup_routes(app.at("/containers/"));
app
}

View File

@ -6,5 +6,5 @@ pub enum Error {
Pool(#[from] diesel::r2d2::PoolError),
#[error("database migration failed: {0}")]
Migration(#[from] diesel::migration::RunMigrationsError),
Migration(Box<dyn std::error::Error + Send + Sync>),
}

View File

@ -4,7 +4,6 @@ mod error;
#[rustfmt::skip]
pub mod schema;
use crate::embedded_migrations;
pub use error::Error;
use std::env;
@ -12,13 +11,18 @@ use std::env;
use diesel::pg::PgConnection;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
pub(super) fn new() -> Result<Pool<ConnectionManager<PgConnection>>, Error> {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = Pool::new(ConnectionManager::new(database_url))?;
let pool = Pool::new(ConnectionManager::<PgConnection>::new(database_url))?;
info!("starting db migrations...");
embedded_migrations::run(&pool.get()?)?;
pool.get()?
.run_pending_migrations(MIGRATIONS)
.map_err(|e| Error::Migration(e))?;
info!("db migrations complete");
Ok(pool)

View File

@ -1,6 +1,19 @@
table! {
containers (id) {
id -> Uuid,
parent -> Nullable<Uuid>,
name -> Varchar,
}
}
table! {
users (id) {
id -> Uuid,
nickname -> Varchar,
}
}
allow_tables_to_appear_in_same_query!(
containers,
users,
);

View File

@ -13,12 +13,6 @@ use std::io;
#[macro_use]
extern crate diesel;
// embed diesel migrations
#[macro_use]
extern crate diesel_migrations;
embed_migrations!();
fn main() -> Result<(), io::Error> {
log::with_level(
std::env::var("LOG")