Skip to main content

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};