#![cfg_attr(not(test), allow(dead_code, unused_imports))]
use std::error::Error;
use std::fs;
use std::path::Path;
use ruststream::asyncapi::{Spec, ViewerOptions, build_spec, render_viewer_html};
use ruststream::memory::prelude::*;
use ruststream::schemars::JsonSchema;
use serde::Deserialize;
#[derive(Debug, Deserialize, JsonSchema)]
struct Order {
id: u64,
item: String,
}
#[subscriber("orders")]
async fn accept(order: &Order) -> HandlerOutcome {
tracing::info!(order.id, item = %order.item, "accepted");
HandlerOutcome::ack()
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}
fn document() -> Spec {
build_spec(&app())
}
fn write_document(dir: &Path) -> Result<(), Box<dyn Error>> {
let spec = build_spec(&app());
fs::write(dir.join("asyncapi.json"), spec.to_json()?)?;
fs::write(dir.join("asyncapi.yaml"), spec.to_yaml()?)?;
Ok(())
}
fn docs_page() -> String {
let options = ViewerOptions::default().title("Orders events");
render_viewer_html("/asyncapi.json", &options)
}
fn main() {}
#[cfg(test)]
mod tests {
use std::error::Error;
use std::fs;
use serde_json::{Value, json};
use super::{docs_page, document, write_document};
#[test]
fn the_subscriber_becomes_a_channel_an_operation_and_a_message() -> Result<(), Box<dyn Error>> {
let spec: Value = serde_json::from_str(&document().to_json()?)?;
assert_eq!(spec["asyncapi"], "3.1.0");
assert_eq!(
spec["info"],
json!({ "title": "orders", "version": "0.1.0" })
);
assert_eq!(spec["channels"]["orders"]["address"], "orders");
let operation = &spec["operations"]["receive_orders"];
assert_eq!(operation["action"], "receive");
assert_eq!(operation["channel"]["$ref"], "#/channels/orders");
assert_eq!(
operation["description"],
"Accepts every order placed in the shop."
);
let order = &spec["components"]["messages"]["Order"];
assert_eq!(order["description"], "An order placed by a customer.");
assert_eq!(order["contentType"], "application/json");
assert_eq!(order["payload"]["required"], json!(["id", "item"]));
Ok(())
}
#[test]
fn the_document_is_written_as_json_and_yaml() -> Result<(), Box<dyn Error>> {
let dir = std::env::temp_dir().join(format!("asyncapi-generation-{}", std::process::id()));
fs::create_dir_all(&dir)?;
write_document(&dir)?;
let json: Value = serde_json::from_str(&fs::read_to_string(dir.join("asyncapi.json"))?)?;
let yaml = fs::read_to_string(dir.join("asyncapi.yaml"))?;
fs::remove_dir_all(&dir)?;
assert_eq!(json["asyncapi"], "3.1.0");
assert!(yaml.starts_with("asyncapi: 3.1.0\n"), "{yaml}");
assert!(yaml.contains("receive_orders:"), "{yaml}");
Ok(())
}
#[test]
fn the_viewer_page_renders_the_served_document() {
let page = docs_page();
assert!(page.contains("<title>Orders events</title>"));
assert!(page.contains("url: \"/asyncapi.json\""));
assert!(
page.contains("https://cdn.jsdelivr.net/npm/@asyncapi/react-component@3.1.8/browser/standalone/index.js")
);
}
}