ocpi_kit/server/mod.rs
1//! An OCPI server: one trait per module and interface, mounted onto an `axum::Router`.
2//!
3//! ```no_run
4//! use std::sync::Arc;
5//! use ocpi_kit::server::{InMemoryTokenStore, OcpiRouter};
6//! use ocpi_kit::types::Url;
7//! use ocpi_kit::VersionNumber;
8//!
9//! # async fn serve(
10//! # credentials: impl ocpi_kit::server::CredentialsHandler,
11//! # locations: impl ocpi_kit::server::LocationsSender,
12//! # ) -> Result<(), Box<dyn std::error::Error>> {
13//! let tokens = Arc::new(InMemoryTokenStore::new());
14//!
15//! let app = OcpiRouter::new(
16//! VersionNumber::V2_3_0,
17//! Url::new("https://cpo.example.com/ocpi/cpo/2.3.0")?,
18//! tokens,
19//! )
20//! .credentials(credentials)
21//! .locations_sender(locations)
22//! .build();
23//!
24//! let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
25//! axum::serve(listener, app).await?;
26//! # Ok(())
27//! # }
28//! ```
29//!
30//! # What the router takes care of
31//!
32//! * **The status code rules.** Only five situations get an HTTP error status; everything that
33//! reached the OCPI layer is a `200 OK` with a four-digit code in the body. A handler returns
34//! [`OcpiError`](crate::transport::OcpiError) and the mapping happens once, correctly.
35//! * **Authentication, and the `CREDENTIALS_TOKEN_A` scope.** A bootstrap token used on any
36//! module other than `credentials` and `versions` gets a 401, as the specification requires.
37//! * **Ownership of client-owned objects.** A platform writing under a `country_code`/`party_id`
38//! that is not one of its own roles gets a 404 — *"this way blocking client access to objects
39//! that do not belong to them"* — and the handler is never called.
40//! * **`X-Request-ID` and `X-Correlation-ID`.** Echoed on every response, generated when the peer
41//! forgot them.
42//! * **Version details.** `/versions` and the version-details endpoint are generated from exactly
43//! what was mounted, so discovery cannot disagree with reality.
44//! * **The PATCH rule.** A patch without `last_updated` never reaches a handler; it is the
45//! specification's own example of a `2001`.
46//!
47//! # What it deliberately leaves to you
48//!
49//! Persistence, and the two credentials 405 rules — only the implementation knows whether a peer
50//! is already registered. [`PeerState`](crate::client::PeerState) has the predicates for those.
51
52mod auth;
53mod bridge;
54mod error;
55mod extract;
56mod router;
57mod traits;
58
59pub use auth::{AuthenticatedPeer, InMemoryTokenStore, MountedModules, PeerRegistry, TokenStore};
60pub use error::{HttpStatusCode, OcpiErrorResponse, OcpiReply, echo_ids};
61pub use extract::{
62 Auth, AuthState, ContentTypePolicy, Ids, OcpiJson, OcpiPatch, Owner, Page, PagePolicy, RequestContext,
63 Routing, accepts_json, reject,
64};
65pub use router::{CallbackUrls, OcpiRouter, OcpiState, ServerConfig};
66pub use traits::{
67 CdrsReceiver, CdrsSender, ChargingProfilesReceiver, ChargingProfilesSender, CommandsReceiver,
68 CommandsSender, Created, CredentialsHandler, Handled, HubClientInfoReceiver, HubClientInfoSender,
69 LocationsReceiver, LocationsSender, PaymentsReceiver, PaymentsSender, SessionsReceiver, SessionsSender,
70 TariffsReceiver, TariffsSender, TokensReceiver, TokensSender,
71};