htsget_lambda/
lib.rs

1//! htsget-lambda only has functionality to run the Lambda handler. Please use `htsget-axum`
2//! for similar functionality on routers and logic.
3//!
4
5use futures::TryFuture;
6use htsget_axum::server::ticket::TicketServer;
7use htsget_config::config::Config;
8use htsget_config::package_info;
9use lambda_http::request::LambdaRequest;
10use lambda_http::{
11  Error, IntoResponse, LambdaEvent, Request, RequestExt, Service, TransformResponse, lambda_runtime,
12};
13use std::marker::PhantomData;
14use std::path::Path;
15use std::task::{Context, Poll};
16use tracing::debug;
17
18/// Wraps the htsget-axum router to forward any Lambda event extensions to the router.
19pub struct Adapter<'a, R, S> {
20  service: S,
21  _phantom_data: PhantomData<&'a R>,
22}
23
24impl<'a, R, S, E> From<S> for Adapter<'a, R, S>
25where
26  S: Service<Request, Response = R, Error = E>,
27  S::Future: Send + 'a,
28  R: IntoResponse,
29{
30  fn from(service: S) -> Self {
31    Adapter {
32      service,
33      _phantom_data: PhantomData,
34    }
35  }
36}
37
38impl<'a, R, S, E> Service<LambdaEvent<serde_json::Value>> for Adapter<'a, R, S>
39where
40  S: Service<Request, Response = R, Error = E>,
41  S::Future: Send + 'a,
42  R: IntoResponse,
43{
44  type Response = <TransformResponse<'a, R, E> as TryFuture>::Ok;
45  type Error = E;
46  type Future = TransformResponse<'a, R, Self::Error>;
47
48  fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
49    self.service.poll_ready(cx)
50  }
51
52  fn call(&mut self, req: LambdaEvent<serde_json::Value>) -> Self::Future {
53    // The original request is consumed when creating a future, so clone it here.
54    let original_request = req.payload.clone();
55
56    let lambda_request: LambdaRequest =
57      serde_json::from_value(req.payload).expect("invalid payload when deserializing to json");
58    let request_origin = lambda_request.request_origin();
59    let mut event: Request = lambda_request.into();
60
61    // After creating the request event, add the original request as an extension.
62    debug!(original_request = ?original_request, "original_request");
63    event.extensions_mut().insert(original_request);
64
65    let fut = Box::pin(self.service.call(event.with_lambda_context(req.context)));
66    TransformResponse::Request(request_origin, fut)
67  }
68}
69
70/// Run the Lambda handler using the config file contained at the path.
71pub async fn run_handler(path: &Path) -> Result<(), Error> {
72  let mut config = Config::from_path(path)?;
73  config.set_package_info(package_info!())?;
74  config.setup_tracing()?;
75
76  debug!(config = ?config, "config parsed");
77
78  let service_info = config.service_info().clone();
79  let cors = config.ticket_server().cors().clone();
80  let auth = config.ticket_server().auth().cloned();
81  let package_info = config.package_info().clone();
82  let router = TicketServer::router(
83    config.into_locations(),
84    service_info,
85    cors,
86    auth,
87    Some(package_info),
88  )?;
89
90  lambda_runtime::run(Adapter::from(router)).await
91}