Define handler in terms of CloudEvents rather than http/payload (#412)

This should simplify the unit tests. Only the invalid_event test uses
the actix-web test helpers, just as an example to show it's the HTTP
plumbing that will fail the request when it tries to construct an
Event from invalid (or missing) headers.
This commit is contained in:
Jim Crossley 2021-07-12 09:04:13 -04:00 committed by GitHub
parent 795cd7251e
commit c2c1b999e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 66 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "actix-codec"
version = "0.3.0"
@ -448,15 +450,20 @@ dependencies = [
[[package]]
name = "cloudevents-sdk"
version = "0.3.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7c9f6054fbd2d641a2d275e44f922a3816a6cf51751fb0f72fcf26e27209dfd"
checksum = "1f86076fd3d8c3c3449f29cc8f41d5831504870620077307c1dcbae9f7861fa0"
dependencies = [
"actix-web",
"async-trait",
"base64 0.12.3",
"bitflags",
"bytes 1.0.1",
"chrono",
"delegate-attr",
"futures",
"hostname",
"lazy_static",
"serde",
"serde_json",
"snafu",
@ -465,20 +472,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "cloudevents-sdk-actix-web"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbeee2dc837554a8e57b20025ba175241bae2cfe27b078bedf27c9da668d35a"
dependencies = [
"actix-web",
"async-trait",
"bytes 0.5.6",
"cloudevents-sdk",
"futures",
"lazy_static",
]
[[package]]
name = "const_fn"
version = "0.4.8"
@ -661,7 +654,6 @@ dependencies = [
"actix-rt",
"actix-web",
"cloudevents-sdk",
"cloudevents-sdk-actix-web",
"env_logger",
"log",
"serde_json",

View File

@ -5,8 +5,7 @@ version = "0.1.0"
edition = "2018"
[dependencies]
cloudevents-sdk = "0.3.0"
cloudevents-sdk-actix-web = "0.3.0"
cloudevents-sdk = { version = "0.4", features = ["actix"] }
actix-web = "3"
actix-rt = "1"
serde_json = "1"

View File

@ -1,74 +1,58 @@
use actix_web::{web, HttpRequest, HttpResponse};
use cloudevents::{event::Data, EventBuilder, EventBuilderV10};
use cloudevents_sdk_actix_web::{HttpRequestExt, HttpResponseBuilderExt};
use cloudevents::{event::Data, Event, EventBuilder, EventBuilderV10};
use log::info;
use serde_json::json;
use serde_json::{from_slice, from_str, json};
// Implement your function's logic here
pub async fn handle(req: HttpRequest, pl: web::Payload) -> Result<HttpResponse, actix_web::Error> {
let event = req.to_event(pl).await?;
info!("request: {}", event);
pub async fn handle(event: Event) -> Result<Event, actix_web::Error> {
info!("event: {}", event);
let input = if let Some(Data::Binary(data)) = event.data() {
serde_json::from_slice(data).unwrap()
} else {
json!({ "name": "world" })
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": "world" }),
};
let response = EventBuilderV10::from(event)
EventBuilderV10::from(event)
.source("func://handler")
.ty("func.example")
.data("application/json", json!({ "hello": input["name"] }))
.build()
.map_err(actix_web::error::ErrorInternalServerError)?;
info!("response: {}", response);
HttpResponse::Ok().event(response).await
.map_err(actix_web::error::ErrorInternalServerError)
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{body::Body, test};
#[actix_rt::test]
async fn valid_input() {
let input = json!({"name": "bootsy"});
let (req, payload) = test::TestRequest::post()
.header("ce-specversion", "1.0")
.header("ce-id", "1")
.header("ce-type", "test")
.header("ce-source", "http://localhost")
.set_json(&input)
.to_http_parts();
let resp = handle(req, web::Payload(payload)).await.unwrap();
assert!(resp.status().is_success());
assert_eq!(
&Body::from(json!({"hello":"bootsy"})),
resp.body().as_ref().unwrap()
);
let mut input = Event::default();
input.set_data("application/json", json!({"name": "bootsy"}));
let resp = handle(input).await;
assert!(resp.is_ok());
match resp.unwrap().data() {
Some(Data::Json(output)) => assert_eq!("bootsy", output["hello"]),
_ => panic!(),
}
}
#[actix_rt::test]
async fn no_input() {
let (req, payload) = test::TestRequest::post()
.header("ce-specversion", "1.0")
.header("ce-id", "1")
.header("ce-type", "test")
.header("ce-source", "http://localhost")
.to_http_parts();
let resp = handle(req, web::Payload(payload)).await.unwrap();
assert!(resp.status().is_success());
assert_eq!(
&Body::from(json!({"hello":"world"})),
resp.body().as_ref().unwrap()
);
let resp = handle(Event::default()).await;
assert!(resp.is_ok());
match resp.unwrap().data() {
Some(Data::Json(output)) => assert_eq!("world", output["hello"]),
_ => panic!(),
}
}
#[actix_rt::test]
async fn invalid_event() {
let (req, payload) = test::TestRequest::post().to_http_parts();
let resp = handle(req, web::Payload(payload)).await;
assert!(resp.is_err());
use actix_web::{test, web, App};
let mut app = test::init_service(App::new().route("/", web::post().to(handle))).await;
let req = test::TestRequest::post().uri("/").to_request();
let resp = test::call_service(&mut app, req).await;
assert!(resp.status().is_client_error());
}
}