seuss 0.3.1

A batteries-included framework for implementing Redfish-compliant services.
Documentation
use std::str::FromStr;

use axum::{
    extract::{OriginalUri, State},
    http::StatusCode,
    response::Response,
    Extension, Json, Router,
};
use redfish_axum::{session::Session, session_collection::SessionCollection};
use redfish_codegen::models::{
    odata_v4, resource, session::v1_6_0::Session as SessionModel,
    session_collection::SessionCollection as SessionCollectionModel,
    session_service::v1_1_8::SessionService as SessionServiceModel,
};
use redfish_core::auth::AuthenticateRequest;

use crate::{auth::SessionManagement, middleware::ResourceLocator};

#[derive(Clone)]
pub struct SessionService<M, A>
where
    M: Clone + SessionManagement,
    A: Clone + AuthenticateRequest + 'static,
{
    session_manager: M,
    auth_handler: A,
}

impl<M, A> AsRef<dyn AuthenticateRequest> for SessionService<M, A>
where
    M: Clone + SessionManagement,
    A: Clone + AuthenticateRequest + 'static,
{
    fn as_ref(&self) -> &(dyn AuthenticateRequest + 'static) {
        &self.auth_handler
    }
}

impl<M, A> SessionService<M, A>
where
    M: Clone + SessionManagement + Send + Sync + 'static,
    <M as SessionManagement>::Id: FromStr + Clone + Send + Sync,
    A: Clone + AuthenticateRequest + Send + Sync + 'static,
{
    pub fn new(session_manager: M, auth_handler: A) -> Self {
        Self {
            auth_handler,
            session_manager,
        }
    }

    pub fn into_router<S>(self) -> Router<S> {
        redfish_axum::session_service::SessionService::default()
        .get(|OriginalUri(uri): OriginalUri| async move {
            crate::Response(SessionServiceModel {
                id: resource::Id("sessions".to_string()),
                name: resource::Name("SessionService".to_string()),
                odata_id: odata_v4::Id(uri.path().to_string()),
                sessions: Some(odata_v4::IdRef {
                    odata_id: Some(odata_v4::Id(uri.path().to_string() + "/Sessions")),
                }),
                ..Default::default()
            })
        })
        .sessions(
            SessionCollection::default()
                .get(
                    |OriginalUri(uri): OriginalUri, State(state): State<Self>| async move {
                        match state.session_manager.sessions() {
                            Ok(members) => Ok(crate::Response(SessionCollectionModel {
                                name: resource::Name("SessionCollection".to_string()),
                                odata_id: odata_v4::Id(uri.path().to_string()),
                                members_odata_count: odata_v4::Count(members.len().try_into().unwrap()),
                                members,
                                ..Default::default()
                            })),
                            Err(error) => Err(crate::Response(error)),
                        }
                    },
                )
                .post(
                    |State(mut state): State<Self>, OriginalUri(uri): OriginalUri, Json(session): Json<SessionModel>| async move {
                        match state.session_manager.create_session(session, uri.path().to_string()) {
                            Ok(session) => Ok((
                                StatusCode::CREATED,
                                [
                                    ("X-Auth-Token", session.token.clone().unwrap()),
                                    ("Location", session.odata_id.0.clone()),
                                ],
                                crate::Response(session),
                            )),
                            Err(error) => Err(crate::Response(error)),
                        }
                    },
                )
                .session(
                    Session::default()
                        .get(|Extension(id): Extension<M::Id>, State(state): State<Self>| async move {
                            match state.session_manager.get_session(id) {
                                Ok(session) => Ok(crate::Response(session)),
                                Err(error) => Err(crate::Response(error)),
                            }
                        })
                        .delete(|Extension(id): Extension<M::Id>, State(mut state): State<Self>| async move {
                            match state.session_manager.delete_session(id) {
                                Ok(()) => Ok(StatusCode::NO_CONTENT),
                                Err(error) => Err(crate::Response(error)),
                            }
                        })
                        .into_router()
                        .route_layer(ResourceLocator::new(
                            "session_id".to_string(),
                            |id: M::Id| async move { Ok::<_, Response>(id) }
                        ))
                )
                .into_router()
        )
        .into_router()
        .with_state(self)
    }
}