WASI example (#228)
* feat: wasi-example Signed-off-by: Anton Whalley <anton@venshare.com> * fix: remove line end Signed-off-by: Anton Whalley <anton@venshare.com> --------- Signed-off-by: Anton Whalley <anton@venshare.com>
This commit is contained in:
parent
13c36fdbfe
commit
2f57c3ce36
|
@ -115,6 +115,14 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CARGO_TARGET_WASM32_WASI_RUNNER: wasmedge
|
CARGO_TARGET_WASM32_WASI_RUNNER: wasmedge
|
||||||
# Build examples
|
# Build examples
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
name: "Build wasi-example"
|
||||||
|
if: matrix.target == 'wasm32-wasi' && matrix.toolchain == 'stable'
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
toolchain: ${{ matrix.toolchain }}
|
||||||
|
args: --target ${{ matrix.target }} --manifest-path ./example-projects/wasi-example/Cargo.toml
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
name: "Build reqwest-wasm-example"
|
name: "Build reqwest-wasm-example"
|
||||||
if: matrix.target == 'wasm32-unknown-unknown' && matrix.toolchain == 'stable'
|
if: matrix.target == 'wasm32-unknown-unknown' && matrix.toolchain == 'stable'
|
||||||
|
@ -170,3 +178,4 @@ jobs:
|
||||||
command: build
|
command: build
|
||||||
toolchain: ${{ matrix.toolchain }}
|
toolchain: ${{ matrix.toolchain }}
|
||||||
args: --target ${{ matrix.target }} --manifest-path ./example-projects/nats-example/Cargo.toml
|
args: --target ${{ matrix.target }} --manifest-path ./example-projects/nats-example/Cargo.toml
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "wasi-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
cloudevents-sdk = { path = "../..", features = ["http-binding", "hyper_wasi", "hyper" ] }
|
||||||
|
hyper_wasi = { version = "0.15", features = ["full"] }
|
||||||
|
log = "0.4.21"
|
||||||
|
tokio_wasi = { version = "1", features = ["io-util", "fs", "net", "time", "rt", "macros"] }
|
||||||
|
serde_json = " 1.0.116"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bytes = "1.6.0"
|
||||||
|
http-body-util = "0.1.1"
|
||||||
|
chrono = "*"
|
|
@ -0,0 +1,26 @@
|
||||||
|
Install WASMEdge:
|
||||||
|
|
||||||
|
https://wasmedge.org/docs/start/install/
|
||||||
|
|
||||||
|
To run the server:
|
||||||
|
```console
|
||||||
|
cargo run --target wasm32-wasi
|
||||||
|
```
|
||||||
|
|
||||||
|
To test a GET:
|
||||||
|
|
||||||
|
```console
|
||||||
|
curl -sw '%{http_code}\n' http://localhost:9000/health/readiness
|
||||||
|
```
|
||||||
|
|
||||||
|
To test a POST:
|
||||||
|
|
||||||
|
```console
|
||||||
|
curl -d '{"name": "wasi-womble"}' \
|
||||||
|
-H'content-type: application/json' \
|
||||||
|
-H'ce-specversion: 1.0' \
|
||||||
|
-H'ce-id: 1' \
|
||||||
|
-H'ce-source: http://cloudevents.io' \
|
||||||
|
-H'ce-type: dev.knative.example' \
|
||||||
|
http://localhost:9000
|
||||||
|
```
|
|
@ -0,0 +1,39 @@
|
||||||
|
use cloudevents::{event::Data, Event, EventBuilder, EventBuilderV10};
|
||||||
|
use log::info;
|
||||||
|
use serde_json::{from_slice, from_str, json};
|
||||||
|
|
||||||
|
pub async fn handle_event(event: Event) -> Result<Event, anyhow::Error> {
|
||||||
|
info!("event: {}", event);
|
||||||
|
|
||||||
|
let input = match event.data() {
|
||||||
|
Some(Data::Binary(v)) => from_slice(v)?,
|
||||||
|
Some(Data::String(v)) => from_str(v)?,
|
||||||
|
Some(Data::Json(v)) => v.to_owned(),
|
||||||
|
None => json!({ "name": "default" }),
|
||||||
|
};
|
||||||
|
|
||||||
|
EventBuilderV10::from(event)
|
||||||
|
.source("func://handler")
|
||||||
|
.ty("func.example")
|
||||||
|
.data("application/json", json!({ "hello": input["name"] }))
|
||||||
|
.build()
|
||||||
|
.map_err(|err| err.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[tokio::test]
|
||||||
|
async fn post_test() -> Result<(), anyhow::Error> {
|
||||||
|
let reqevt = Event::default();
|
||||||
|
let respevt = handle_event(reqevt).await?;
|
||||||
|
let output = match respevt.data() {
|
||||||
|
Some(Data::Binary(v)) => from_slice(v)?,
|
||||||
|
Some(Data::String(v)) => from_str(v)?,
|
||||||
|
Some(Data::Json(v)) => v.to_owned(),
|
||||||
|
None => json!({ "name": "default" }),
|
||||||
|
};
|
||||||
|
assert_eq!(output, json!({ "hello": "default" }));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
use cloudevents::binding::http::builder::adapter::to_response;
|
||||||
|
use cloudevents::binding::http::to_event;
|
||||||
|
|
||||||
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::Server;
|
||||||
|
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||||
|
use std::convert::Infallible;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
mod handler;
|
||||||
|
|
||||||
|
#[allow(clippy::redundant_closure)]
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let addr = SocketAddr::from(([0, 0, 0, 0], 9000));
|
||||||
|
let make_svc = make_service_fn(|_| async move {
|
||||||
|
Ok::<_, Infallible>(service_fn(move |req| handle_request(req)))
|
||||||
|
});
|
||||||
|
let server = Server::bind(&addr).serve(make_svc);
|
||||||
|
if let Err(e) = server.await {
|
||||||
|
eprintln!("server error: {}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn handle_request(req: Request<Body>) -> Result<Response<Body>, anyhow::Error> {
|
||||||
|
match (req.method(), req.uri().path()) {
|
||||||
|
(&Method::POST, "/") => {
|
||||||
|
let headers = req.headers().clone();
|
||||||
|
let body_bytes = hyper::body::to_bytes(req.into_body()).await?;
|
||||||
|
let body = body_bytes.to_vec();
|
||||||
|
let reqevt = to_event(&headers, body)?;
|
||||||
|
let _respevt = handler::handle_event(reqevt).await?;
|
||||||
|
|
||||||
|
to_response(_respevt).map_err(|err| err.into())
|
||||||
|
}
|
||||||
|
(&Method::GET, "/health/readiness") => Ok(Response::new(Body::from(""))),
|
||||||
|
(&Method::GET, "/health/liveness") => Ok(Response::new(Body::from(""))),
|
||||||
|
_ => {
|
||||||
|
let mut not_found = Response::default();
|
||||||
|
*not_found.status_mut() = StatusCode::NOT_FOUND;
|
||||||
|
Ok(not_found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue