feat(hemx): clarify generated authoring path
Close the fallible handler and example coherence slice: v0 uses typed todo newtypes, Result handler failure mapping, generated form/partial helpers, and page composition; kanban is explicitly marked as an advanced north-star milestone; raw render is isolated behind hemx::advanced::render. req: canonical_authoring/003 req: failure/004 req: public_api/005 req: examples/004
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use axum::extract::State;
|
||||
use axum::http::{header, HeaderMap};
|
||||
use axum::response::IntoResponse;
|
||||
use scraper::{Html, Selector};
|
||||
use hemx_axum::{
|
||||
interactions, runtime_js, DispatchRejection, EffectResponse, Form, InteractionForm,
|
||||
InteractionFormRejection, InteractionRequest, PageMode, PageRequest, PageResponse,
|
||||
HEMX_CONTENT_TYPE, HEMX_FINGERPRINT_HEADER, HEMX_PARTIAL_HEADER, HEMX_RUNTIME_CONTENT_TYPE,
|
||||
HEMX_TITLE_HEADER,
|
||||
interactions, runtime_js, DispatchRejection, EffectResponse, Form, HandlerErrorContext,
|
||||
HandlerFailure, InteractionForm, InteractionFormRejection, InteractionRequest,
|
||||
IntoHandlerFailure, PageMode, PageRequest, PageResponse, HEMX_CONTENT_TYPE,
|
||||
HEMX_FINGERPRINT_HEADER, HEMX_PARTIAL_HEADER, HEMX_RUNTIME_CONTENT_TYPE, HEMX_TITLE_HEADER,
|
||||
};
|
||||
use hemx_core::{push, BuildFingerprint, Handle, IntoEffect, SafeHtml, Slot};
|
||||
use scraper::{Html, Selector};
|
||||
|
||||
fn selector(value: &str) -> Selector {
|
||||
Selector::parse(value).expect("test selector parses")
|
||||
@@ -306,6 +306,21 @@ impl std::fmt::Display for HandlerBoom {
|
||||
|
||||
impl std::error::Error for HandlerBoom {}
|
||||
|
||||
impl IntoHandlerFailure for HandlerBoom {
|
||||
fn into_handler_failure(self, _context: HandlerErrorContext) -> HandlerFailure {
|
||||
HandlerFailure::internal(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UiFailure;
|
||||
|
||||
impl IntoHandlerFailure for UiFailure {
|
||||
fn into_handler_failure(self, context: HandlerErrorContext) -> HandlerFailure {
|
||||
HandlerFailure::effects(Slot::<String>::new(3).text("try again"), context)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interaction_request_dispatches_async_typed_state_extractors() {
|
||||
// req: axum_integration/003 req: form/004 req: canonical_authoring/003
|
||||
@@ -359,10 +374,69 @@ async fn interaction_request_reports_async_result_handler_errors() {
|
||||
|
||||
assert_eq!(
|
||||
error,
|
||||
DispatchRejection::HandlerError {
|
||||
handle_id: 11,
|
||||
DispatchRejection::HandlerError(HandlerFailure::Response {
|
||||
status: axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
message: "database unavailable".to_owned(),
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interaction_request_maps_async_result_errors_to_effects() {
|
||||
// req: axum_integration/003 req: failure/004
|
||||
async fn open_project(
|
||||
State(_multiplier): State<u32>,
|
||||
Form(_input): Form<OpenProject>,
|
||||
) -> Result<impl IntoEffect, UiFailure> {
|
||||
Err::<(), _>(UiFailure)
|
||||
}
|
||||
|
||||
let request = InteractionRequest::from(InteractionForm::for_handle(
|
||||
Handle::<()>::new(12),
|
||||
vec![("project_id".to_owned(), "21".to_owned())],
|
||||
));
|
||||
let response = request
|
||||
.dispatch_async(
|
||||
interactions(BuildFingerprint(4))
|
||||
.with_state(2_u32)
|
||||
.on_async_result(Handle::<()>::new(12), open_project)
|
||||
.into_registry(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
response.batch.ops,
|
||||
vec![Slot::<String>::new(3).text("try again")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interaction_request_maps_sync_result_errors_to_effects() {
|
||||
// req: axum_integration/003 req: failure/004
|
||||
fn open_project(
|
||||
State(_multiplier): State<u32>,
|
||||
Form(_input): Form<OpenProject>,
|
||||
) -> Result<impl IntoEffect, UiFailure> {
|
||||
Err::<(), _>(UiFailure)
|
||||
}
|
||||
|
||||
let request = InteractionRequest::from(InteractionForm::for_handle(
|
||||
Handle::<()>::new(13),
|
||||
vec![("project_id".to_owned(), "21".to_owned())],
|
||||
));
|
||||
let response = request
|
||||
.dispatch(
|
||||
interactions(BuildFingerprint(4))
|
||||
.with_state(2_u32)
|
||||
.on_result(Handle::<()>::new(13), open_project)
|
||||
.into_registry(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
response.batch.ops,
|
||||
vec![Slot::<String>::new(3).text("try again")]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user