221 lines
5.4 KiB
Rust
221 lines
5.4 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use mongod::{reference_of, vec_to_api, Model, Referencable, Sort, ToAPI};
|
|
use mongodb::bson::doc;
|
|
use rocket::{get, post, serde::json::Json, State};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::json;
|
|
|
|
use crate::{
|
|
check_auth,
|
|
config::Config,
|
|
flow::{Flow, FlowInfo, FlowNote},
|
|
json_store::JSONStore,
|
|
routes::{api_error, FallibleApiResponse, Token},
|
|
transaction::{Price, Transaction},
|
|
};
|
|
|
|
use super::ApiError;
|
|
|
|
#[get("/flow/<id>/info")]
|
|
pub async fn flow_info(
|
|
id: &str,
|
|
flows: &State<JSONStore<FlowInfo>>,
|
|
t: Token,
|
|
c: &State<Config>,
|
|
) -> FallibleApiResponse {
|
|
check_auth!(t, c);
|
|
|
|
let flowinfo = flows.get(id).ok_or_else(|| api_error("Flow not found"))?;
|
|
|
|
Ok(flowinfo.api().await)
|
|
}
|
|
|
|
#[get("/flow/<id>/active")]
|
|
pub async fn active_flows_route(
|
|
id: &str,
|
|
flows: &State<JSONStore<FlowInfo>>,
|
|
t: Token,
|
|
c: &State<Config>,
|
|
) -> FallibleApiResponse {
|
|
check_auth!(t, c);
|
|
|
|
let flowinfo = flows.get(id).ok_or_else(|| api_error("Flow not found"))?;
|
|
|
|
let flow = Flow::find(
|
|
doc! {
|
|
"kind": flowinfo.reference(),
|
|
"done": { "$not": { "$type": "object" } }
|
|
},
|
|
None,
|
|
None,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
Ok(json!(vec_to_api(&flow).await))
|
|
}
|
|
|
|
#[get("/flows")]
|
|
pub async fn flows_list(
|
|
flows: &State<JSONStore<FlowInfo>>,
|
|
t: Token,
|
|
c: &State<Config>,
|
|
) -> FallibleApiResponse {
|
|
check_auth!(t, c);
|
|
|
|
let mut ret = HashMap::<String, serde_json::Value>::new();
|
|
|
|
for l in flows.iter() {
|
|
ret.insert(l.0.clone(), l.1.api().await);
|
|
}
|
|
|
|
Ok(json!(ret))
|
|
}
|
|
|
|
pub async fn create_flow(
|
|
kind: &str,
|
|
flows: &State<JSONStore<FlowInfo>>,
|
|
form: &CreateFlow,
|
|
) -> Result<Flow, ApiError> {
|
|
let info = flows.get(kind).ok_or_else(|| api_error("Unknown Flow"))?;
|
|
|
|
let mut input_ref = Vec::new();
|
|
|
|
// verify valid input transactions
|
|
if let Some(input) = &form.input {
|
|
for t in input {
|
|
let t = Transaction::get(t)
|
|
.await
|
|
.ok_or_else(|| api_error(&format!("No such transaction {t}")))?;
|
|
|
|
let item_variant = format!("{}::{}", t.item, t.variant);
|
|
|
|
let valid = info.depends.iter().any(|x| item_variant.starts_with(x));
|
|
|
|
if !valid {
|
|
return Err(api_error(&format!(
|
|
"Invalid item variant. This flow only accepts {:?}",
|
|
info.depends
|
|
)));
|
|
}
|
|
|
|
input_ref.push(t);
|
|
}
|
|
}
|
|
|
|
let flow = Flow::create(
|
|
kind,
|
|
if input_ref.is_empty() {
|
|
None
|
|
} else {
|
|
Some(input_ref.iter().map(|x| x.reference()).collect())
|
|
},
|
|
)
|
|
.await;
|
|
|
|
for t in input_ref {
|
|
t.consume(Price::zero(), &format!("flow::{kind}::{}", flow._id))
|
|
.await;
|
|
}
|
|
|
|
Ok(flow)
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CreateFlow {
|
|
pub input: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct EndFlow {
|
|
pub produced: Option<Vec<HashMap<String, u64>>>,
|
|
}
|
|
|
|
#[post("/flow/<id>", data = "<form>")]
|
|
pub async fn create_flow_route(
|
|
id: &str,
|
|
form: Json<CreateFlow>,
|
|
flows: &State<JSONStore<FlowInfo>>,
|
|
) -> FallibleApiResponse {
|
|
let flow = create_flow(id, flows, &form).await?;
|
|
Ok(json!({"uuid": flow._id }))
|
|
}
|
|
|
|
#[get("/flow/<id>")]
|
|
pub async fn flow_api_route(id: &str) -> FallibleApiResponse {
|
|
let flow = Flow::get(id)
|
|
.await
|
|
.ok_or_else(|| api_error("No such flow"))?;
|
|
Ok(flow.api().await)
|
|
}
|
|
|
|
#[post("/flow/<id>/end", data = "<form>")]
|
|
pub async fn end_flow_route(id: &str, form: Json<EndFlow>) -> FallibleApiResponse {
|
|
let flow = Flow::get(id)
|
|
.await
|
|
.ok_or_else(|| api_error("Flow not found"))?;
|
|
|
|
if let Some(produced) = &form.produced {
|
|
let prod = flow.end_with_produce(produced).await?;
|
|
Ok(json!({"produced": prod}))
|
|
} else {
|
|
flow.end().await;
|
|
Ok(json!({"ok": 1}))
|
|
}
|
|
}
|
|
|
|
#[post("/flow/<id>/continue", data = "<form>")]
|
|
pub async fn continue_flow_route(
|
|
id: &str,
|
|
flows: &State<JSONStore<FlowInfo>>,
|
|
form: Json<CreateFlow>,
|
|
) -> FallibleApiResponse {
|
|
let this_flow = Flow::get(id)
|
|
.await
|
|
.ok_or_else(|| api_error("Flow not found"))?;
|
|
|
|
// create next flow
|
|
let next_kind = flows
|
|
.get(this_flow.kind.id())
|
|
.ok_or_else(|| api_error("Flow not found"))?
|
|
.next
|
|
.clone()
|
|
.ok_or_else(|| api_error("Flow has no continuation"))?;
|
|
let next_flow = create_flow(&next_kind, flows, &form).await?;
|
|
|
|
// end current flow
|
|
this_flow.continue_next(&next_flow).await;
|
|
|
|
Ok(json!({"uuid": next_flow._id}))
|
|
}
|
|
|
|
#[get("/flow/<id>/notes")]
|
|
pub async fn flow_notes_route(id: &str) -> FallibleApiResponse {
|
|
let notes = FlowNote::find(
|
|
doc! {
|
|
"on_flow": reference_of!(Flow, id).ok_or_else(|| api_error("No such flow"))?
|
|
},
|
|
None,
|
|
Some(doc! { "timestamp": Sort::Descending }),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
Ok(json!(vec_to_api(¬es).await))
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NoteAdd {
|
|
pub content: String,
|
|
}
|
|
|
|
#[post("/flow/<id>/note", data = "<form>")]
|
|
pub async fn create_flow_note_route(id: &str, form: Json<NoteAdd>) -> FallibleApiResponse {
|
|
let note = FlowNote::create(
|
|
&form.content,
|
|
reference_of!(Flow, id).ok_or_else(|| api_error("No such flow"))?,
|
|
)
|
|
.await;
|
|
Ok(json!({"uuid": note._id }))
|
|
}
|