From 739836fd11eee7a4ee855bd8324e739f6bcdeec8 Mon Sep 17 00:00:00 2001 From: Patrick Meade Date: Wed, 26 Jun 2024 22:15:11 -0500 Subject: [PATCH] First draft at routes disks and events --- README.md | 40 ++++++++++++++++++++ doc/TODO.md | 13 +++++++ src/main.rs | 16 +++++++- src/routes.rs | 3 ++ src/routes/v1.rs | 17 +++++++++ src/routes/v1/disks.rs | 84 +++++++++++++++++++++++++++++++++++++++++ src/routes/v1/events.rs | 56 +++++++++++++++++++++++++++ 7 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 doc/TODO.md create mode 100644 src/routes.rs create mode 100644 src/routes/v1.rs create mode 100644 src/routes/v1/disks.rs create mode 100644 src/routes/v1/events.rs diff --git a/README.md b/README.md index 012746f..7403179 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ # wipac-disk-tracking Archival media tracking service for WIPAC + +## API +The REST API for the wipac-disk-tracking service is documented below. + +The API is rooted at: + + /api/v# + +Where # is the version number of the API under use. + +### Disks +Disks represent a unique archival disk. +While the disk entity does carry some immutable identifying information, it +is mostly a container for archival disk events. + +#### Routes +These routes are implemented to work with disks: + + GET /disks/:disk_id Get the data for a given disk + GET /disks/:disk_id/events Get all of the events for a given disk + GET /disks/:disk_id/events/:event_id Get the data for a given event for a given disk + GET /disks/:disk_id/search?query Find a disk based on a key-value query + +### Events +Events represent a record of an event involving an archival disk. +There are four distinct events that are tracked by the system. + + sighted This disk was observed to be loaded in a host that processes archival disks + formatted This disk was given a file system to make it ready for archival purposes + opened This disk was given a label and was designated for active archival activity + closed This disk was determined to full/finished and archival activity stopped + +The format for each of these events is specified with a JSON Schema file. + +#### Routes +These routes are implemented to work with events: + + POST /events Create a new event + GET /events/:event_id Get the data for a given event + GET /events/search?query Find an event based on key-value query diff --git a/doc/TODO.md b/doc/TODO.md new file mode 100644 index 0000000..633ee5f --- /dev/null +++ b/doc/TODO.md @@ -0,0 +1,13 @@ +# TODO.md +Some notes to keep track of progress + +## JSON Schema +Look at [JSON Schema](https://json-schema.org/) and in particular some validators written in Rust: + +https://json-schema.org/implementations#validators-rust + +Rust +boon 2020-122019-09070604 Apache License 2.0 +jsonschema-rs +Fast due to compiling schema into a validation tree; 2019-09 and 2020-12 are partially supported + 2020-122019-09070604 MIT diff --git a/src/main.rs b/src/main.rs index 50e58c2..e006e56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,11 @@ // main.rs -use gotham::state::State; +pub mod routes; + +use gotham::{ + router::{build_simple_router, builder::DrawRoutes, Router}, + state::State, +}; use log::info; const HELLO_WORLD: &str = "Hello World!\n"; @@ -9,13 +14,20 @@ pub fn say_hello(state: State) -> (State, &'static str) { (state, HELLO_WORLD) } +fn build_router() -> Router { + build_simple_router(|route| { + route.delegate("/api/v1").to_router(routes::v1::router()); + }) +} + pub fn main() { // initialize logging, configured by environment env_logger::init(); // start the service let addr = "0.0.0.0:8080"; info!("Listening for requests at http://{}", addr); - gotham::start(addr, || Ok(say_hello)).unwrap(); + // gotham::start(addr, || Ok(say_hello)).unwrap(); + let _ = gotham::start(addr, build_router()); } // -------------------------------------------------------------------------------------------------------------------- diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..c7d5b3b --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,3 @@ +// routes.rs + +pub mod v1; diff --git a/src/routes/v1.rs b/src/routes/v1.rs new file mode 100644 index 0000000..6dd23ce --- /dev/null +++ b/src/routes/v1.rs @@ -0,0 +1,17 @@ +// v1.rs + +pub mod disks; +pub mod events; + +pub use disks::{DiskResource, DiskResources}; +pub use events::{EventResource, EventResources}; + +use gotham::router::{build_simple_router, Router}; +use gotham_restful::DrawResources; + +pub fn router() -> Router { + build_simple_router(|route| { + route.resource::("disks"); + route.resource::("events"); + }) +} diff --git a/src/routes/v1/disks.rs b/src/routes/v1/disks.rs new file mode 100644 index 0000000..5a91f9b --- /dev/null +++ b/src/routes/v1/disks.rs @@ -0,0 +1,84 @@ +// disks.rs + +use gotham::{router::response::StaticResponseExtender, state::StateData}; +use gotham_restful::{endpoint, gotham::hyper::Method, read, search, Resource, Success}; +use log::{error, info}; +use serde::{Deserialize, Serialize}; + +use crate::routes::v1::{EventResource, EventResources}; + +#[derive(Resource, Serialize)] +#[resource(get_disk_by_id)] +#[resource(get_event_by_disk_id_and_event_id)] +#[resource(get_events_by_disk_id)] +#[resource(find_disk_by_query)] +pub struct DiskResource { + pub id: u64, +} + +#[derive(Serialize)] +pub struct DiskResources { + pub disks: Vec, +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] +struct DiskSearchQuery { + serial_number: String, +} + +#[search] +fn find_disk_by_query(query: DiskSearchQuery) -> Success { + info!("find_disk_by_query()"); + DiskResources { + disks: Vec::new() + }.into() +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[read] +fn get_disk_by_id(disk_id: u64) -> Success { + info!("get_disk_by_id()"); + DiskResource { id: disk_id }.into() +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] +struct DiskAndEvent { + disk_id: u64, + event_id: u64, +} + +#[endpoint( + uri = ":disk_id/events/:event_id", + method = "Method::GET", + params = false, + body = false +)] +fn get_event_by_disk_id_and_event_id(disk_and_event: DiskAndEvent) -> Success { + info!("get_event_by_disk_id_and_event_id()"); + EventResource { id: disk_and_event.event_id }.into() +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] +struct Disk { + disk_id: u64, +} + +#[endpoint( + uri = ":disk_id/events", + method = "Method::GET", + params = false, + body = false +)] +fn get_events_by_disk_id(disk: Disk) -> Success { + info!("get_events_by_disk_id()"); + EventResources { + events: Vec::new() + }.into() +} diff --git a/src/routes/v1/events.rs b/src/routes/v1/events.rs new file mode 100644 index 0000000..6c33ac7 --- /dev/null +++ b/src/routes/v1/events.rs @@ -0,0 +1,56 @@ +// events.rs + +use gotham::{router::response::StaticResponseExtender, state::StateData}; +use gotham_restful::{create, read, search, Resource, Success}; +use log::{error, info}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +#[derive(Resource, Serialize)] +#[resource(create_event)] +#[resource(find_event_by_query)] +#[resource(get_event_by_id)] +pub struct EventResource { + pub id: u64, +} + +#[derive(Serialize)] +pub struct EventResources { + pub events: Vec, +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] +struct EventBody { + serial_number: String, +} + +#[create] +fn create_event(body: EventBody) -> Success { + info!("create_event()"); + EventResource { id: 0 }.into() +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[derive(Clone, Deserialize, StateData, StaticResponseExtender)] +struct EventSearchQuery { + serial_number: String, +} + +#[search] +fn find_event_by_query(query: EventSearchQuery) -> Success { + info!("find_event_by_query()"); + EventResources { + events: Vec::new(), + }.into() +} + +// -------------------------------------------------------------------------------------------------------------------- + +#[read] +fn get_event_by_id(id: u64) -> Success { + info!("get_event_by_id()"); + EventResource { id }.into() +}