1use std::sync::Arc;
4
5use axum::Router;
6use axum::extract::{Path, Query, State};
7use axum::routing::{delete, get, post, put};
8
9use crate::transport::{OcpiError, Page, PageMeta, Quirks};
10use crate::types::{PartyRef, Url};
11use crate::{InterfaceRole, ModuleId, VersionNumber};
12
13use super::auth::{MountedModules, TokenStore};
14use super::error::{OcpiErrorResponse, OcpiReply};
15use super::extract::{
16 Auth, AuthState, ContentTypePolicy, Ids, OcpiJson, OcpiPatch, Owner, Page as PageParams, RequestContext,
17 Routing, accepts_json,
18};
19use super::traits::{
20 CdrsReceiver, CdrsSender, ChargingProfilesReceiver, ChargingProfilesSender, CommandsReceiver,
21 CommandsSender, CredentialsHandler, HubClientInfoReceiver, HubClientInfoSender, LocationsReceiver,
22 LocationsSender, PaymentsReceiver, PaymentsSender, SessionsReceiver, SessionsSender, TariffsReceiver,
23 TariffsSender, TokensReceiver, TokensSender,
24};
25
26#[derive(Clone, Debug)]
28#[non_exhaustive]
29pub struct ServerConfig {
30 pub quirks: Quirks,
33 pub max_page_limit: u64,
37 pub receiver_path_prefix: Option<String>,
69}
70
71impl Default for ServerConfig {
72 fn default() -> Self {
73 Self {
74 quirks: Quirks::default(),
75 max_page_limit: 100,
76 receiver_path_prefix: Some("receiver".to_owned()),
77 }
78 }
79}
80
81impl ServerConfig {
82 #[must_use]
84 pub fn with_quirks(mut self, quirks: Quirks) -> Self {
85 self.quirks = quirks;
86 self
87 }
88
89 #[must_use]
92 pub const fn with_max_page_limit(mut self, limit: u64) -> Self {
93 self.max_page_limit = limit;
94 self
95 }
96
97 #[must_use]
99 pub fn with_receiver_path_prefix(mut self, prefix: impl Into<String>) -> Self {
100 self.receiver_path_prefix = Some(prefix.into());
101 self
102 }
103
104 #[must_use]
114 pub fn one_router_per_role(mut self) -> Self {
115 self.receiver_path_prefix = None;
116 self
117 }
118}
119
120pub struct OcpiState {
122 tokens: Arc<dyn TokenStore>,
123 config: ServerConfig,
124 base_url: Url,
125 mounted: MountedModules,
126 version: VersionNumber,
127}
128
129impl OcpiState {
130 #[must_use]
132 pub const fn config(&self) -> &ServerConfig {
133 &self.config
134 }
135
136 #[must_use]
138 pub const fn base_url(&self) -> &Url {
139 &self.base_url
140 }
141
142 #[must_use]
144 pub const fn mounted(&self) -> &MountedModules {
145 &self.mounted
146 }
147
148 #[must_use]
150 pub const fn version(&self) -> &VersionNumber {
151 &self.version
152 }
153
154 #[must_use]
156 pub fn version_details(&self) -> crate::v2_3_0::versions::VersionDetails {
157 let endpoints = self
158 .mounted
159 .all()
160 .iter()
161 .map(|(module, role)| {
162 let mut url = self.base_url.clone();
163 if *role == InterfaceRole::Receiver
164 && let Some(prefix) = &self.config.receiver_path_prefix
165 {
166 url = url.join(prefix);
167 }
168 crate::v2_3_0::versions::Endpoint::new(module.clone(), *role, url.join(module.as_str()))
169 })
170 .collect();
171 crate::v2_3_0::versions::VersionDetails::new(self.version.clone(), endpoints)
172 }
173}
174
175impl AuthState for Arc<OcpiState> {
176 fn tokens(&self) -> &dyn TokenStore {
177 self.tokens.as_ref()
178 }
179 fn quirks(&self) -> &Quirks {
180 &self.config.quirks
181 }
182}
183
184impl super::extract::PagePolicy for Arc<OcpiState> {
185 fn max_page_limit(&self) -> u64 {
186 self.config.max_page_limit
187 }
188}
189
190impl ContentTypePolicy for Arc<OcpiState> {
191 fn accepts_content_type(&self, headers: &http::HeaderMap) -> bool {
192 accepts_json(headers, self.config.quirks.lenient_content_type)
193 }
194}
195
196pub struct OcpiRouter {
222 router: Router<Arc<OcpiState>>,
223 tokens: Arc<dyn TokenStore>,
224 config: ServerConfig,
225 base_url: Url,
226 mounted: MountedModules,
227 version: VersionNumber,
228}
229
230impl OcpiRouter {
231 fn receiver_prefix(&self) -> Option<String> {
233 self.config.receiver_path_prefix.clone()
234 }
235
236 fn check_interface_conflict(&self, module: &ModuleId, mounting: InterfaceRole) {
246 let ambiguous =
250 matches!(module, ModuleId::Locations | ModuleId::ChargingProfiles | ModuleId::Payments);
251 let other =
252 if mounting == InterfaceRole::Sender { InterfaceRole::Receiver } else { InterfaceRole::Sender };
253 assert!(
254 !(ambiguous
255 && self.config.receiver_path_prefix.is_none()
256 && self.mounted.contains(module, other)),
257 "cannot mount both interfaces of the {module} module on one router with no \
258 receiver path prefix: the Sender and Receiver URLs have the same shape, so no \
259 route ordering can tell them apart. Either set \
260 ServerConfig::receiver_path_prefix, or build one OcpiRouter per interface role \
261 and nest them under different base URLs.",
262 );
263 }
264
265 #[must_use]
267 pub fn new(version: VersionNumber, base_url: Url, tokens: Arc<dyn TokenStore>) -> Self {
268 Self {
269 router: Router::new(),
270 tokens,
271 config: ServerConfig::default(),
272 base_url,
273 mounted: MountedModules::new(),
274 version,
275 }
276 }
277
278 #[must_use]
280 pub fn with_config(mut self, config: ServerConfig) -> Self {
281 self.config = config;
282 self
283 }
284
285 #[must_use]
287 pub fn credentials<H: CredentialsHandler>(mut self, handler: H) -> Self {
288 let handler = Arc::new(handler);
289 self.mounted.add(ModuleId::Credentials, InterfaceRole::Sender);
290 let get_handler = Arc::clone(&handler);
291 let post_handler = Arc::clone(&handler);
292 let put_handler = Arc::clone(&handler);
293 let delete_handler = handler;
294 self.router = self.router.route(
295 "/credentials",
296 get(async move |auth: Auth, ids: Ids| -> Result<_, OcpiErrorResponse> {
297 let context = context_of(auth, ids, Routing(None), &ModuleId::Credentials)?;
298 let ids = context.ids.clone();
299 get_handler
300 .get(context)
301 .await
302 .map(|c| OcpiReply::ok(c).with_ids(ids.clone()))
303 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
304 })
305 .post(
306 async move |auth: Auth,
307 ids: Ids,
308 OcpiJson(body): OcpiJson<crate::v2_3_0::credentials::Credentials>|
309 -> Result<_, OcpiErrorResponse> {
310 let context = context_of(auth, ids, Routing(None), &ModuleId::Credentials)?;
311 let ids = context.ids.clone();
312 post_handler
313 .post(body, context)
314 .await
315 .map(|c| OcpiReply::ok(c).with_ids(ids.clone()))
316 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
317 },
318 )
319 .put(
320 async move |auth: Auth,
321 ids: Ids,
322 OcpiJson(body): OcpiJson<crate::v2_3_0::credentials::Credentials>|
323 -> Result<_, OcpiErrorResponse> {
324 let context = context_of(auth, ids, Routing(None), &ModuleId::Credentials)?;
325 let ids = context.ids.clone();
326 put_handler
327 .put(body, context)
328 .await
329 .map(|c| OcpiReply::ok(c).with_ids(ids.clone()))
330 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
331 },
332 )
333 .delete(async move |auth: Auth, ids: Ids| -> Result<_, OcpiErrorResponse> {
334 let context = context_of(auth, ids, Routing(None), &ModuleId::Credentials)?;
335 let ids = context.ids.clone();
336 delete_handler
337 .delete(context)
338 .await
339 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
340 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
341 }),
342 );
343 self
344 }
345
346 #[must_use]
348 pub fn locations_sender<H: LocationsSender>(mut self, handler: H) -> Self {
349 self.check_interface_conflict(&ModuleId::Locations, InterfaceRole::Sender);
350 let handler = Arc::new(handler);
351 self.mounted.add(ModuleId::Locations, InterfaceRole::Sender);
352
353 let list = Arc::clone(&handler);
354 let one = Arc::clone(&handler);
355 let evse = Arc::clone(&handler);
356 let connector = handler;
357
358 self.router = self
359 .router
360 .route(
361 "/locations",
362 get(
363 async move |auth: Auth,
364 ids: Ids,
365 routing: Routing,
366 PageParams(query): PageParams,
367 State(state): State<Arc<OcpiState>>|
368 -> Result<_, OcpiErrorResponse> {
369 let context = context_of(auth, ids, routing, &ModuleId::Locations)?;
370 let ids = context.ids.clone();
371 let responder = context.addressed_to().cloned();
372 let response_routing = responder.as_ref().and_then(|r| context.response_routing(r));
373 list.list(query, context)
374 .await
375 .map(|page| page_reply(page, &state))
376 .map(|reply| {
377 let reply = reply.with_ids(ids.clone());
378 match response_routing {
379 Some(r) => reply.with_routing(r),
380 None => reply,
381 }
382 })
383 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
384 },
385 ),
386 )
387 .route(
388 "/locations/{location_id}",
389 get(
390 async move |auth: Auth,
391 ids: Ids,
392 routing: Routing,
393 Path(location_id): Path<String>|
394 -> Result<_, OcpiErrorResponse> {
395 let context = context_of(auth, ids, routing, &ModuleId::Locations)?;
396 let ids = context.ids.clone();
397 one.location(location_id, context)
398 .await
399 .map(|l| OcpiReply::ok(l).with_ids(ids.clone()))
400 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
401 },
402 ),
403 )
404 .route(
405 "/locations/{location_id}/{evse_uid}",
406 get(
407 async move |auth: Auth,
408 ids: Ids,
409 routing: Routing,
410 Path((location_id, evse_uid)): Path<(String, String)>|
411 -> Result<_, OcpiErrorResponse> {
412 let context = context_of(auth, ids, routing, &ModuleId::Locations)?;
413 let ids = context.ids.clone();
414 evse.evse(location_id, evse_uid, context)
415 .await
416 .map(|e| OcpiReply::ok(e).with_ids(ids.clone()))
417 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
418 },
419 ),
420 )
421 .route(
422 "/locations/{location_id}/{evse_uid}/{connector_id}",
423 get(
424 async move |auth: Auth,
425 ids: Ids,
426 routing: Routing,
427 Path((location_id, evse_uid, connector_id)): Path<(
428 String,
429 String,
430 String,
431 )>|
432 -> Result<_, OcpiErrorResponse> {
433 let context = context_of(auth, ids, routing, &ModuleId::Locations)?;
434 let ids = context.ids.clone();
435 connector
436 .connector(location_id, evse_uid, connector_id, context)
437 .await
438 .map(|c| OcpiReply::ok(c).with_ids(ids.clone()))
439 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
440 },
441 ),
442 );
443 self
444 }
445
446 #[must_use]
448 pub fn locations_receiver<H: LocationsReceiver>(mut self, handler: H) -> Self {
449 let prefix = self.receiver_prefix();
450 self.check_interface_conflict(&ModuleId::Locations, InterfaceRole::Receiver);
451 let handler = Arc::new(handler);
452 self.mounted.add(ModuleId::Locations, InterfaceRole::Receiver);
453
454 let get_one = Arc::clone(&handler);
455 let put_location = Arc::clone(&handler);
456 let put_evse = Arc::clone(&handler);
457 let patch_any = handler;
458
459 self.router = self
460 .router
461 .route(
462 &receiver_path(prefix.as_deref(), "/locations/{country_code}/{party_id}/{location_id}"),
463 get(
464 async move |auth: Auth,
465 ids: Ids,
466 routing: Routing,
467 Path((country_code, party_id, location_id)): Path<(
468 String,
469 String,
470 String,
471 )>|
472 -> Result<_, OcpiErrorResponse> {
473 let (context, owner) = owned_context(
474 auth,
475 ids,
476 routing,
477 &ModuleId::Locations,
478 &country_code,
479 &party_id,
480 )?;
481 let ids = context.ids.clone();
482 get_one
483 .location(owner, location_id, context)
484 .await
485 .map(|l| OcpiReply::ok(l).with_ids(ids.clone()))
486 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
487 },
488 )
489 .put(
490 async move |auth: Auth,
491 ids: Ids,
492 routing: Routing,
493 Path((country_code, party_id, location_id)): Path<(
494 String,
495 String,
496 String,
497 )>,
498 OcpiJson(location): OcpiJson<crate::v2_3_0::locations::Location>|
499 -> Result<_, OcpiErrorResponse> {
500 let (context, owner) = owned_context(
501 auth,
502 ids,
503 routing,
504 &ModuleId::Locations,
505 &country_code,
506 &party_id,
507 )?;
508 let ids = context.ids.clone();
509 if !location.id.eq_ignore_case(&location_id) {
512 return Err(OcpiErrorResponse::new(OcpiError::Decode {
513 path: "/id".to_owned(),
514 message: format!(
515 "the object id {:?} does not match the {location_id:?} in the URL",
516 location.id.as_str()
517 ),
518 })
519 .with_ids(ids));
520 }
521 put_location
522 .put_location(owner, location, context)
523 .await
524 .map(|created| {
525 OcpiReply::<()>::no_content()
526 .with_http_status(status_of(created))
527 .with_ids(ids.clone())
528 })
529 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
530 },
531 )
532 .patch(
533 async move |auth: Auth,
534 ids: Ids,
535 routing: Routing,
536 Path((country_code, party_id, location_id)): Path<(
537 String,
538 String,
539 String,
540 )>,
541 OcpiPatch(patch): OcpiPatch<serde_json::Value>|
542 -> Result<_, OcpiErrorResponse> {
543 let (context, owner) = owned_context(
544 auth,
545 ids,
546 routing,
547 &ModuleId::Locations,
548 &country_code,
549 &party_id,
550 )?;
551 let ids = context.ids.clone();
552 patch_any
553 .patch(owner, location_id, None, None, patch, context)
554 .await
555 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
556 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
557 },
558 ),
559 )
560 .route(
561 &receiver_path(
562 prefix.as_deref(),
563 "/locations/{country_code}/{party_id}/{location_id}/{evse_uid}",
564 ),
565 put(
566 async move |auth: Auth,
567 ids: Ids,
568 routing: Routing,
569 Path((country_code, party_id, location_id, _evse_uid)): Path<(
570 String,
571 String,
572 String,
573 String,
574 )>,
575 OcpiJson(evse): OcpiJson<crate::v2_3_0::locations::Evse>|
576 -> Result<_, OcpiErrorResponse> {
577 let (context, owner) = owned_context(
578 auth,
579 ids,
580 routing,
581 &ModuleId::Locations,
582 &country_code,
583 &party_id,
584 )?;
585 let ids = context.ids.clone();
586 put_evse
587 .put_evse(owner, location_id, evse, context)
588 .await
589 .map(|created| {
590 OcpiReply::<()>::no_content()
591 .with_http_status(status_of(created))
592 .with_ids(ids.clone())
593 })
594 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
595 },
596 ),
597 );
598 self
599 }
600
601 #[must_use]
603 pub fn tokens_sender<H: TokensSender>(mut self, handler: H) -> Self {
604 let handler = Arc::new(handler);
605 self.mounted.add(ModuleId::Tokens, InterfaceRole::Sender);
606 let list = Arc::clone(&handler);
607 let authorize = handler;
608
609 self.router = self
610 .router
611 .route(
612 "/tokens",
613 get(
614 async move |auth: Auth,
615 ids: Ids,
616 routing: Routing,
617 PageParams(query): PageParams,
618 State(state): State<Arc<OcpiState>>|
619 -> Result<_, OcpiErrorResponse> {
620 let context = context_of(auth, ids, routing, &ModuleId::Tokens)?;
621 let ids = context.ids.clone();
622 list.list(query, context)
623 .await
624 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
625 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
626 },
627 ),
628 )
629 .route(
630 "/tokens/{token_uid}/authorize",
631 post(
632 async move |auth: Auth,
633 ids: Ids,
634 routing: Routing,
635 Path(token_uid): Path<String>,
636 Query(params): Query<TokenTypeQuery>,
637 body: axum::body::Bytes|
638 -> Result<_, OcpiErrorResponse> {
639 let context = context_of(auth, ids, routing, &ModuleId::Tokens)?;
640 let ids = context.ids.clone();
641 let location =
645 decode_optional_body::<crate::v2_3_0::tokens::LocationReferences>(&body)
646 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids.clone()))?;
647 authorize
648 .authorize(token_uid, params.token_type, location, context)
649 .await
650 .map(|info| OcpiReply::ok(info).with_ids(ids.clone()))
651 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
652 },
653 ),
654 );
655 self
656 }
657
658 #[must_use]
660 pub fn tokens_receiver<H: TokensReceiver>(mut self, handler: H) -> Self {
661 let prefix = self.receiver_prefix();
662 let handler = Arc::new(handler);
663 self.mounted.add(ModuleId::Tokens, InterfaceRole::Receiver);
664 let get_one = Arc::clone(&handler);
665 let put_one = Arc::clone(&handler);
666 let patch_one = handler;
667
668 self.router = self.router.route(
669 &receiver_path(prefix.as_deref(), "/tokens/{country_code}/{party_id}/{token_uid}"),
670 get(
671 async move |auth: Auth,
672 ids: Ids,
673 routing: Routing,
674 Path((country_code, party_id, token_uid)): Path<(String, String, String)>,
675 Query(params): Query<TokenTypeQuery>|
676 -> Result<_, OcpiErrorResponse> {
677 let (context, owner) =
678 owned_context(auth, ids, routing, &ModuleId::Tokens, &country_code, &party_id)?;
679 let ids = context.ids.clone();
680 get_one
681 .token(owner, token_uid, params.token_type, context)
682 .await
683 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
684 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
685 },
686 )
687 .put(
688 async move |auth: Auth,
689 ids: Ids,
690 routing: Routing,
691 Path((country_code, party_id, _token_uid)): Path<(String, String, String)>,
692 OcpiJson(token): OcpiJson<crate::v2_3_0::tokens::Token>|
693 -> Result<_, OcpiErrorResponse> {
694 let (context, owner) =
695 owned_context(auth, ids, routing, &ModuleId::Tokens, &country_code, &party_id)?;
696 let ids = context.ids.clone();
697 put_one
698 .put_token(owner, token, context)
699 .await
700 .map(|created| {
701 OcpiReply::<()>::no_content()
702 .with_http_status(status_of(created))
703 .with_ids(ids.clone())
704 })
705 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
706 },
707 )
708 .patch(
709 async move |auth: Auth,
710 ids: Ids,
711 routing: Routing,
712 Path((country_code, party_id, token_uid)): Path<(String, String, String)>,
713 Query(params): Query<TokenTypeQuery>,
714 OcpiPatch(patch): OcpiPatch<crate::v2_3_0::tokens::Token>|
715 -> Result<_, OcpiErrorResponse> {
716 let (context, owner) =
717 owned_context(auth, ids, routing, &ModuleId::Tokens, &country_code, &party_id)?;
718 let ids = context.ids.clone();
719 patch_one
720 .patch_token(owner, token_uid, params.token_type, patch, context)
721 .await
722 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
723 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
724 },
725 ),
726 );
727 self
728 }
729
730 #[must_use]
732 pub fn cdrs_sender<H: CdrsSender>(mut self, handler: H) -> Self {
733 let handler = Arc::new(handler);
734 self.mounted.add(ModuleId::Cdrs, InterfaceRole::Sender);
735 self.router = self.router.route(
736 "/cdrs",
737 get(
738 async move |auth: Auth,
739 ids: Ids,
740 routing: Routing,
741 PageParams(query): PageParams,
742 State(state): State<Arc<OcpiState>>|
743 -> Result<_, OcpiErrorResponse> {
744 let context = context_of(auth, ids, routing, &ModuleId::Cdrs)?;
745 let ids = context.ids.clone();
746 handler
747 .list(query, context)
748 .await
749 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
750 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
751 },
752 ),
753 );
754 self
755 }
756
757 #[must_use]
759 pub fn cdrs_receiver<H: CdrsReceiver>(mut self, handler: H) -> Self {
760 let prefix = self.receiver_prefix();
761 let handler = Arc::new(handler);
762 self.mounted.add(ModuleId::Cdrs, InterfaceRole::Receiver);
763 let get_one = Arc::clone(&handler);
764 let post_one = handler;
765
766 self.router = self
767 .router
768 .route(
769 &receiver_path(prefix.as_deref(), "/cdrs"),
770 post(
771 async move |auth: Auth,
772 ids: Ids,
773 routing: Routing,
774 OcpiJson(cdr): OcpiJson<crate::v2_3_0::cdrs::Cdr>|
775 -> Result<_, OcpiErrorResponse> {
776 let context = context_of(auth, ids, routing, &ModuleId::Cdrs)?;
777 let ids = context.ids.clone();
778 post_one
779 .post_cdr(cdr, context)
780 .await
781 .map(|location| {
782 let mut headers = http::HeaderMap::new();
783 if let Ok(value) = http::HeaderValue::from_str(location.as_str()) {
784 headers.insert(crate::transport::headers::LOCATION, value);
785 }
786 OcpiReply::<()>::no_content()
787 .with_http_status(http::StatusCode::CREATED)
788 .with_headers(headers)
789 .with_ids(ids.clone())
790 })
791 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
792 },
793 ),
794 )
795 .route(
796 &receiver_path(prefix.as_deref(), "/cdrs/{cdr_id}"),
797 get(
798 async move |auth: Auth,
799 ids: Ids,
800 routing: Routing,
801 Path(cdr_id): Path<String>|
802 -> Result<_, OcpiErrorResponse> {
803 let context = context_of(auth, ids, routing, &ModuleId::Cdrs)?;
804 let ids = context.ids.clone();
805 get_one
806 .cdr(cdr_id, context)
807 .await
808 .map(|c| OcpiReply::ok(c).with_ids(ids.clone()))
809 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
810 },
811 ),
812 );
813 self
814 }
815
816 #[must_use]
818 pub fn sessions_sender<H: SessionsSender>(mut self, handler: H) -> Self {
819 let handler = Arc::new(handler);
820 self.mounted.add(ModuleId::Sessions, InterfaceRole::Sender);
821 let list = Arc::clone(&handler);
822 let preferences = handler;
823
824 self.router = self
825 .router
826 .route(
827 "/sessions",
828 get(
829 async move |auth: Auth,
830 ids: Ids,
831 routing: Routing,
832 PageParams(query): PageParams,
833 State(state): State<Arc<OcpiState>>|
834 -> Result<_, OcpiErrorResponse> {
835 let context = context_of(auth, ids, routing, &ModuleId::Sessions)?;
836 let ids = context.ids.clone();
837 list.list(query, context)
838 .await
839 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
840 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
841 },
842 ),
843 )
844 .route(
845 "/sessions/{session_id}/charging_preferences",
846 put(
847 async move |auth: Auth,
848 ids: Ids,
849 routing: Routing,
850 Path(session_id): Path<String>,
851 OcpiJson(body): OcpiJson<crate::v2_3_0::sessions::ChargingPreferences>|
852 -> Result<_, OcpiErrorResponse> {
853 let context = context_of(auth, ids, routing, &ModuleId::Sessions)?;
854 let ids = context.ids.clone();
855 preferences
856 .set_charging_preferences(session_id, body, context)
857 .await
858 .map(|r| OcpiReply::ok(r).with_ids(ids.clone()))
859 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
860 },
861 ),
862 );
863 self
864 }
865
866 #[must_use]
868 pub fn sessions_receiver<H: SessionsReceiver>(mut self, handler: H) -> Self {
869 let prefix = self.receiver_prefix();
870 let handler = Arc::new(handler);
871 self.mounted.add(ModuleId::Sessions, InterfaceRole::Receiver);
872 let get_one = Arc::clone(&handler);
873 let put_one = Arc::clone(&handler);
874 let patch_one = handler;
875
876 self.router = self.router.route(
877 &receiver_path(prefix.as_deref(), "/sessions/{country_code}/{party_id}/{session_id}"),
878 get(
879 async move |auth: Auth,
880 ids: Ids,
881 routing: Routing,
882 Path((country_code, party_id, session_id)): Path<(String, String, String)>|
883 -> Result<_, OcpiErrorResponse> {
884 let (context, owner) =
885 owned_context(auth, ids, routing, &ModuleId::Sessions, &country_code, &party_id)?;
886 let ids = context.ids.clone();
887 get_one
888 .session(owner, session_id, context)
889 .await
890 .map(|s| OcpiReply::ok(s).with_ids(ids.clone()))
891 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
892 },
893 )
894 .put(
895 async move |auth: Auth,
896 ids: Ids,
897 routing: Routing,
898 Path((country_code, party_id, _session_id)): Path<(String, String, String)>,
899 OcpiJson(session): OcpiJson<crate::v2_3_0::sessions::Session>|
900 -> Result<_, OcpiErrorResponse> {
901 let (context, owner) =
902 owned_context(auth, ids, routing, &ModuleId::Sessions, &country_code, &party_id)?;
903 let ids = context.ids.clone();
904 put_one
905 .put_session(owner, session, context)
906 .await
907 .map(|created| {
908 OcpiReply::<()>::no_content()
909 .with_http_status(status_of(created))
910 .with_ids(ids.clone())
911 })
912 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
913 },
914 )
915 .patch(
916 async move |auth: Auth,
917 ids: Ids,
918 routing: Routing,
919 Path((country_code, party_id, session_id)): Path<(String, String, String)>,
920 OcpiPatch(patch): OcpiPatch<crate::v2_3_0::sessions::Session>|
921 -> Result<_, OcpiErrorResponse> {
922 let (context, owner) =
923 owned_context(auth, ids, routing, &ModuleId::Sessions, &country_code, &party_id)?;
924 let ids = context.ids.clone();
925 patch_one
926 .patch_session(owner, session_id, patch, context)
927 .await
928 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
929 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
930 },
931 ),
932 );
933 self
934 }
935
936 #[must_use]
938 pub fn tariffs_sender<H: TariffsSender>(mut self, handler: H) -> Self {
939 let handler = Arc::new(handler);
940 self.mounted.add(ModuleId::Tariffs, InterfaceRole::Sender);
941 self.router = self.router.route(
942 "/tariffs",
943 get(
944 async move |auth: Auth,
945 ids: Ids,
946 routing: Routing,
947 PageParams(query): PageParams,
948 State(state): State<Arc<OcpiState>>|
949 -> Result<_, OcpiErrorResponse> {
950 let context = context_of(auth, ids, routing, &ModuleId::Tariffs)?;
951 let ids = context.ids.clone();
952 handler
953 .list(query, context)
954 .await
955 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
956 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
957 },
958 ),
959 );
960 self
961 }
962
963 #[must_use]
965 pub fn tariffs_receiver<H: TariffsReceiver>(mut self, handler: H) -> Self {
966 let prefix = self.receiver_prefix();
967 let handler = Arc::new(handler);
968 self.mounted.add(ModuleId::Tariffs, InterfaceRole::Receiver);
969 let get_one = Arc::clone(&handler);
970 let put_one = Arc::clone(&handler);
971 let delete_one = handler;
972
973 self.router = self.router.route(
974 &receiver_path(prefix.as_deref(), "/tariffs/{country_code}/{party_id}/{tariff_id}"),
975 get(
976 async move |auth: Auth,
977 ids: Ids,
978 routing: Routing,
979 Path((country_code, party_id, tariff_id)): Path<(String, String, String)>|
980 -> Result<_, OcpiErrorResponse> {
981 let (context, owner) =
982 owned_context(auth, ids, routing, &ModuleId::Tariffs, &country_code, &party_id)?;
983 let ids = context.ids.clone();
984 get_one
985 .tariff(owner, tariff_id, context)
986 .await
987 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
988 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
989 },
990 )
991 .put(
992 async move |auth: Auth,
993 ids: Ids,
994 routing: Routing,
995 Path((country_code, party_id, _tariff_id)): Path<(String, String, String)>,
996 OcpiJson(tariff): OcpiJson<crate::v2_3_0::tariffs::Tariff>|
997 -> Result<_, OcpiErrorResponse> {
998 let (context, owner) =
999 owned_context(auth, ids, routing, &ModuleId::Tariffs, &country_code, &party_id)?;
1000 let ids = context.ids.clone();
1001 put_one
1002 .put_tariff(owner, tariff, context)
1003 .await
1004 .map(|created| {
1005 OcpiReply::<()>::no_content()
1006 .with_http_status(status_of(created))
1007 .with_ids(ids.clone())
1008 })
1009 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1010 },
1011 ),
1012 );
1013 self.router = self.router.route(
1014 &receiver_path(prefix.as_deref(), "/tariffs/{country_code}/{party_id}/{tariff_id}/"),
1015 delete(
1016 async move |auth: Auth,
1017 ids: Ids,
1018 routing: Routing,
1019 Path((country_code, party_id, tariff_id)): Path<(String, String, String)>|
1020 -> Result<_, OcpiErrorResponse> {
1021 let (context, owner) =
1022 owned_context(auth, ids, routing, &ModuleId::Tariffs, &country_code, &party_id)?;
1023 let ids = context.ids.clone();
1024 delete_one
1025 .delete_tariff(owner, tariff_id, context)
1026 .await
1027 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
1028 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1029 },
1030 ),
1031 );
1032 self
1033 }
1034
1035 #[must_use]
1037 pub fn commands_receiver<H: CommandsReceiver>(mut self, handler: H) -> Self {
1038 let handler = Arc::new(handler);
1039 self.mounted.add(ModuleId::Commands, InterfaceRole::Receiver);
1040 self.router = self.router.route(
1041 "/commands/{command}",
1042 post(
1043 async move |auth: Auth,
1044 ids: Ids,
1045 routing: Routing,
1046 Path(command_name): Path<String>,
1047 body: axum::body::Bytes|
1048 -> Result<_, OcpiErrorResponse> {
1049 let context = context_of(auth, ids, routing, &ModuleId::Commands)?;
1050 let ids = context.ids.clone();
1051 let command = parse_command(&command_name, &body)
1052 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids.clone()))?;
1053 handler
1054 .command(command, context)
1055 .await
1056 .map(|r| OcpiReply::ok(r).with_ids(ids.clone()))
1057 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1058 },
1059 ),
1060 );
1061 self
1062 }
1063
1064 #[must_use]
1068 pub fn hub_client_info_sender<H: HubClientInfoSender>(mut self, handler: H) -> Self {
1069 let handler = Arc::new(handler);
1070 self.mounted.add(ModuleId::HubClientInfo, InterfaceRole::Sender);
1071 self.router = self.router.route(
1072 "/hubclientinfo",
1073 get(
1074 async move |auth: Auth,
1075 ids: Ids,
1076 PageParams(query): PageParams,
1077 State(state): State<Arc<OcpiState>>|
1078 -> Result<_, OcpiErrorResponse> {
1079 let context = context_of(auth, ids, Routing(None), &ModuleId::HubClientInfo)?;
1080 let ids = context.ids.clone();
1081 handler
1082 .list(query, context)
1083 .await
1084 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
1085 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1086 },
1087 ),
1088 );
1089 self
1090 }
1091
1092 #[must_use]
1099 pub fn hub_client_info_receiver<H: HubClientInfoReceiver>(mut self, handler: H) -> Self {
1100 let prefix = self.receiver_prefix();
1101 let handler = Arc::new(handler);
1102 self.mounted.add(ModuleId::HubClientInfo, InterfaceRole::Receiver);
1103 let get_one = Arc::clone(&handler);
1104 let put_one = handler;
1105
1106 self.router = self.router.route(
1107 &receiver_path(prefix.as_deref(), "/hubclientinfo/{country_code}/{party_id}"),
1108 get(
1109 async move |auth: Auth,
1110 ids: Ids,
1111 Path((country_code, party_id)): Path<(String, String)>|
1112 -> Result<_, OcpiErrorResponse> {
1113 let request_ids = ids.0.clone();
1114 let context = context_of(auth, ids, Routing(None), &ModuleId::HubClientInfo)?;
1115 let Owner(party) = Owner::from_path(&country_code, &party_id)
1116 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids.clone()))?;
1117 get_one
1118 .client_info(party, context)
1119 .await
1120 .map(|info| OcpiReply::ok(info).with_ids(request_ids.clone()))
1121 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids))
1122 },
1123 )
1124 .put(
1125 async move |auth: Auth,
1126 ids: Ids,
1127 Path((country_code, party_id)): Path<(String, String)>,
1128 OcpiJson(info): OcpiJson<crate::v2_3_0::hub_client_info::ClientInfo>|
1129 -> Result<_, OcpiErrorResponse> {
1130 let request_ids = ids.0.clone();
1131 let context = context_of(auth, ids, Routing(None), &ModuleId::HubClientInfo)?;
1132 let Owner(party) = Owner::from_path(&country_code, &party_id)
1133 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids.clone()))?;
1134 put_one
1135 .put_client_info(party, info, context)
1136 .await
1137 .map(|created| {
1138 OcpiReply::<()>::no_content()
1139 .with_http_status(status_of(created))
1140 .with_ids(request_ids.clone())
1141 })
1142 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids))
1143 },
1144 ),
1145 );
1146 self
1147 }
1148
1149 #[must_use]
1158 pub fn commands_sender<H: CommandsSender>(mut self, handler: H) -> Self {
1159 let handler = Arc::new(handler);
1160 self.mounted.add(ModuleId::Commands, InterfaceRole::Sender);
1161 self.router = self.router.route(
1162 "/commands/{command}/{unique_id}",
1163 post(
1164 async move |auth: Auth,
1165 ids: Ids,
1166 routing: Routing,
1167 Path((_command, unique_id)): Path<(String, String)>,
1168 OcpiJson(result): OcpiJson<crate::v2_3_0::commands::CommandResult>|
1169 -> Result<_, OcpiErrorResponse> {
1170 let context = context_of(auth, ids, routing, &ModuleId::Commands)?;
1171 let ids = context.ids.clone();
1172 handler
1173 .command_result(unique_id, result, context)
1174 .await
1175 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
1176 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1177 },
1178 ),
1179 );
1180 self
1181 }
1182
1183 #[must_use]
1185 pub fn charging_profiles_receiver<H: ChargingProfilesReceiver>(mut self, handler: H) -> Self {
1186 self.check_interface_conflict(&ModuleId::ChargingProfiles, InterfaceRole::Receiver);
1187 let prefix = self.receiver_prefix();
1188 let handler = Arc::new(handler);
1189 self.mounted.add(ModuleId::ChargingProfiles, InterfaceRole::Receiver);
1190 let get_one = Arc::clone(&handler);
1191 let put_one = Arc::clone(&handler);
1192 let delete_one = handler;
1193
1194 self.router = self.router.route(
1195 &receiver_path(prefix.as_deref(), "/chargingprofiles/{session_id}"),
1196 get(
1197 async move |auth: Auth,
1198 ids: Ids,
1199 routing: Routing,
1200 Path(session_id): Path<String>,
1201 Query(q): Query<ActiveProfileQuery>|
1202 -> Result<_, OcpiErrorResponse> {
1203 let request_ids = ids.0.clone();
1204 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1205 let response_url = q
1206 .response_url
1207 .ok_or_else(|| missing_query("response_url"))
1208 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids.clone()))?;
1209 get_one
1210 .active_charging_profile(session_id, q.duration.unwrap_or(0), response_url, context)
1211 .await
1212 .map(|r| OcpiReply::ok(r).with_ids(request_ids.clone()))
1213 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids))
1214 },
1215 )
1216 .put(
1217 async move |auth: Auth,
1218 ids: Ids,
1219 routing: Routing,
1220 Path(session_id): Path<String>,
1221 OcpiJson(request): OcpiJson<
1222 crate::v2_3_0::charging_profiles::SetChargingProfile,
1223 >|
1224 -> Result<_, OcpiErrorResponse> {
1225 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1226 let ids = context.ids.clone();
1227 put_one
1228 .set_charging_profile(session_id, request, context)
1229 .await
1230 .map(|r| OcpiReply::ok(r).with_ids(ids.clone()))
1231 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1232 },
1233 )
1234 .delete(
1235 async move |auth: Auth,
1236 ids: Ids,
1237 routing: Routing,
1238 Path(session_id): Path<String>,
1239 Query(q): Query<ActiveProfileQuery>|
1240 -> Result<_, OcpiErrorResponse> {
1241 let request_ids = ids.0.clone();
1242 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1243 let response_url = q
1244 .response_url
1245 .ok_or_else(|| missing_query("response_url"))
1246 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids.clone()))?;
1247 delete_one
1248 .clear_charging_profile(session_id, response_url, context)
1249 .await
1250 .map(|r| OcpiReply::ok(r).with_ids(request_ids.clone()))
1251 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids))
1252 },
1253 ),
1254 );
1255 self
1256 }
1257
1258 #[must_use]
1264 pub fn charging_profiles_sender<H: ChargingProfilesSender>(mut self, handler: H) -> Self {
1265 self.check_interface_conflict(&ModuleId::ChargingProfiles, InterfaceRole::Sender);
1266 let handler = Arc::new(handler);
1267 self.mounted.add(ModuleId::ChargingProfiles, InterfaceRole::Sender);
1268 let active = Arc::clone(&handler);
1269 let set = Arc::clone(&handler);
1270 let clear = Arc::clone(&handler);
1271 let pushed = handler;
1272
1273 self.router = self
1274 .router
1275 .route(
1276 "/chargingprofiles/result/active/{unique_id}",
1277 post(
1278 async move |auth: Auth,
1279 ids: Ids,
1280 routing: Routing,
1281 Path(unique_id): Path<String>,
1282 OcpiJson(result): OcpiJson<
1283 crate::v2_3_0::charging_profiles::ActiveChargingProfileResult,
1284 >|
1285 -> Result<_, OcpiErrorResponse> {
1286 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1287 let ids = context.ids.clone();
1288 active
1289 .active_charging_profile_result(unique_id, result, context)
1290 .await
1291 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
1292 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1293 },
1294 ),
1295 )
1296 .route(
1297 "/chargingprofiles/result/set/{unique_id}",
1298 post(
1299 async move |auth: Auth,
1300 ids: Ids,
1301 routing: Routing,
1302 Path(unique_id): Path<String>,
1303 OcpiJson(result): OcpiJson<
1304 crate::v2_3_0::charging_profiles::ChargingProfileResult,
1305 >|
1306 -> Result<_, OcpiErrorResponse> {
1307 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1308 let ids = context.ids.clone();
1309 set.charging_profile_result(unique_id, result, context)
1310 .await
1311 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
1312 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1313 },
1314 ),
1315 )
1316 .route(
1317 "/chargingprofiles/result/clear/{unique_id}",
1318 post(
1319 async move |auth: Auth,
1320 ids: Ids,
1321 routing: Routing,
1322 Path(unique_id): Path<String>,
1323 OcpiJson(result): OcpiJson<
1324 crate::v2_3_0::charging_profiles::ClearProfileResult,
1325 >|
1326 -> Result<_, OcpiErrorResponse> {
1327 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1328 let ids = context.ids.clone();
1329 clear
1330 .clear_profile_result(unique_id, result, context)
1331 .await
1332 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
1333 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1334 },
1335 ),
1336 )
1337 .route(
1338 "/chargingprofiles/{session_id}",
1339 put(
1340 async move |auth: Auth,
1341 ids: Ids,
1342 routing: Routing,
1343 Path(session_id): Path<String>,
1344 OcpiJson(profile): OcpiJson<
1345 crate::v2_3_0::charging_profiles::ActiveChargingProfile,
1346 >|
1347 -> Result<_, OcpiErrorResponse> {
1348 let context = context_of(auth, ids, routing, &ModuleId::ChargingProfiles)?;
1349 let ids = context.ids.clone();
1350 pushed
1351 .put_active_charging_profile(session_id, profile, context)
1352 .await
1353 .map(|()| OcpiReply::<()>::no_content().with_ids(ids.clone()))
1354 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1355 },
1356 ),
1357 );
1358 self
1359 }
1360
1361 #[must_use]
1363 pub fn payments_sender<H: PaymentsSender>(mut self, handler: H) -> Self {
1364 self.check_interface_conflict(&ModuleId::Payments, InterfaceRole::Sender);
1365 let handler = Arc::new(handler);
1366 self.mounted.add(ModuleId::Payments, InterfaceRole::Sender);
1367 let list = Arc::clone(&handler);
1368 let one = Arc::clone(&handler);
1369 let put_one = Arc::clone(&handler);
1370 let patch_one = Arc::clone(&handler);
1371 let activate = Arc::clone(&handler);
1372 let deactivate = Arc::clone(&handler);
1373 let fac_list = Arc::clone(&handler);
1374 let fac_one = handler;
1375
1376 self.router = self
1377 .router
1378 .route(
1379 "/payments/terminals",
1380 get(
1381 async move |auth: Auth,
1382 ids: Ids,
1383 routing: Routing,
1384 PageParams(query): PageParams,
1385 State(state): State<Arc<OcpiState>>|
1386 -> Result<_, OcpiErrorResponse> {
1387 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1388 let ids = context.ids.clone();
1389 list.terminals(query, context)
1390 .await
1391 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
1392 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1393 },
1394 ),
1395 )
1396 .route(
1399 "/payments/terminals/activate",
1400 post(
1401 async move |auth: Auth,
1402 ids: Ids,
1403 routing: Routing,
1404 body: axum::body::Bytes|
1405 -> Result<_, OcpiErrorResponse> {
1406 let request_ids = ids.0.clone();
1407 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1408 let terminal = partial_object(&body)
1412 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids.clone()))?;
1413 activate
1414 .activate_terminal(terminal, context)
1415 .await
1416 .map(|t| OcpiReply::ok(t).with_ids(request_ids.clone()))
1417 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids))
1418 },
1419 ),
1420 )
1421 .route(
1422 "/payments/terminals/{terminal_id}",
1423 get(
1424 async move |auth: Auth,
1425 ids: Ids,
1426 routing: Routing,
1427 Path(terminal_id): Path<String>|
1428 -> Result<_, OcpiErrorResponse> {
1429 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1430 let ids = context.ids.clone();
1431 one.terminal(terminal_id, context)
1432 .await
1433 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
1434 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1435 },
1436 )
1437 .put(
1438 async move |auth: Auth,
1439 ids: Ids,
1440 routing: Routing,
1441 Path(terminal_id): Path<String>,
1442 OcpiJson(terminal): OcpiJson<crate::v2_3_0::payments::Terminal>|
1443 -> Result<_, OcpiErrorResponse> {
1444 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1445 let ids = context.ids.clone();
1446 put_one
1447 .put_terminal(terminal_id, terminal, context)
1448 .await
1449 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
1450 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1451 },
1452 )
1453 .patch(
1454 async move |auth: Auth,
1455 ids: Ids,
1456 routing: Routing,
1457 Path(terminal_id): Path<String>,
1458 OcpiPatch(patch): OcpiPatch<crate::v2_3_0::payments::Terminal>|
1459 -> Result<_, OcpiErrorResponse> {
1460 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1461 let ids = context.ids.clone();
1462 patch_one
1463 .patch_terminal(terminal_id, patch, context)
1464 .await
1465 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
1466 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1467 },
1468 ),
1469 )
1470 .route(
1471 "/payments/terminals/{terminal_id}/deactivate",
1472 post(
1473 async move |auth: Auth,
1474 ids: Ids,
1475 routing: Routing,
1476 Path(terminal_id): Path<String>|
1477 -> Result<_, OcpiErrorResponse> {
1478 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1479 let ids = context.ids.clone();
1480 deactivate
1481 .deactivate_terminal(terminal_id, context)
1482 .await
1483 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
1484 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1485 },
1486 ),
1487 )
1488 .route(
1489 "/payments/financial-advice-confirmations",
1490 get(
1491 async move |auth: Auth,
1492 ids: Ids,
1493 routing: Routing,
1494 PageParams(query): PageParams,
1495 State(state): State<Arc<OcpiState>>|
1496 -> Result<_, OcpiErrorResponse> {
1497 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1498 let ids = context.ids.clone();
1499 fac_list
1500 .financial_advice_confirmations(query, context)
1501 .await
1502 .map(|page| page_reply(page, &state).with_ids(ids.clone()))
1503 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1504 },
1505 ),
1506 )
1507 .route(
1508 "/payments/financial-advice-confirmations/{id}",
1509 get(
1510 async move |auth: Auth,
1511 ids: Ids,
1512 routing: Routing,
1513 Path(id): Path<String>|
1514 -> Result<_, OcpiErrorResponse> {
1515 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1516 let ids = context.ids.clone();
1517 fac_one
1518 .financial_advice_confirmation(id, context)
1519 .await
1520 .map(|f| OcpiReply::ok(f).with_ids(ids.clone()))
1521 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1522 },
1523 ),
1524 );
1525 self
1526 }
1527
1528 #[must_use]
1533 pub fn payments_receiver<H: PaymentsReceiver>(mut self, handler: H) -> Self {
1534 self.check_interface_conflict(&ModuleId::Payments, InterfaceRole::Receiver);
1535 let prefix = self.receiver_prefix();
1536 let handler = Arc::new(handler);
1537 self.mounted.add(ModuleId::Payments, InterfaceRole::Receiver);
1538 let get_terminal = Arc::clone(&handler);
1539 let post_terminal = Arc::clone(&handler);
1540 let get_fac = Arc::clone(&handler);
1541 let post_fac = handler;
1542
1543 self.router = self
1544 .router
1545 .route(
1546 &receiver_path(prefix.as_deref(), "/payments/terminals"),
1547 post(
1548 async move |auth: Auth,
1549 ids: Ids,
1550 routing: Routing,
1551 OcpiJson(terminal): OcpiJson<crate::v2_3_0::payments::Terminal>|
1552 -> Result<_, OcpiErrorResponse> {
1553 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1554 let ids = context.ids.clone();
1555 post_terminal
1556 .post_terminal(terminal, context)
1557 .await
1558 .map(|t| OcpiReply::created(t).with_ids(ids.clone()))
1559 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1560 },
1561 ),
1562 )
1563 .route(
1564 &receiver_path(prefix.as_deref(), "/payments/terminals/{terminal_id}"),
1565 get(
1566 async move |auth: Auth,
1567 ids: Ids,
1568 routing: Routing,
1569 Path(terminal_id): Path<String>|
1570 -> Result<_, OcpiErrorResponse> {
1571 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1572 let ids = context.ids.clone();
1573 get_terminal
1574 .terminal(terminal_id, context)
1575 .await
1576 .map(|t| OcpiReply::ok(t).with_ids(ids.clone()))
1577 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1578 },
1579 ),
1580 )
1581 .route(
1582 &receiver_path(prefix.as_deref(), "/payments/financial-advice-confirmations"),
1583 post(
1584 async move |auth: Auth,
1585 ids: Ids,
1586 routing: Routing,
1587 OcpiJson(confirmation): OcpiJson<
1588 crate::v2_3_0::payments::FinancialAdviceConfirmation,
1589 >|
1590 -> Result<_, OcpiErrorResponse> {
1591 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1592 let ids = context.ids.clone();
1593 post_fac
1594 .post_financial_advice_confirmation(confirmation, context)
1595 .await
1596 .map(|f| OcpiReply::created(f).with_ids(ids.clone()))
1597 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1598 },
1599 ),
1600 )
1601 .route(
1602 &receiver_path(prefix.as_deref(), "/payments/financial-advice-confirmations/{id}"),
1603 get(
1604 async move |auth: Auth,
1605 ids: Ids,
1606 routing: Routing,
1607 Path(id): Path<String>|
1608 -> Result<_, OcpiErrorResponse> {
1609 let context = context_of(auth, ids, routing, &ModuleId::Payments)?;
1610 let ids = context.ids.clone();
1611 get_fac
1612 .financial_advice_confirmation(id, context)
1613 .await
1614 .map(|f| OcpiReply::ok(f).with_ids(ids.clone()))
1615 .map_err(|e| OcpiErrorResponse::new(e).with_ids(ids))
1616 },
1617 ),
1618 );
1619 self
1620 }
1621
1622 pub fn build(self) -> Router {
1634 assert!(
1635 crate::convert::wire::bridgeable(&crate::CANONICAL_VERSION, &self.version),
1636 "this build cannot serve OCPI {}: the handler traits speak the canonical {} model and \
1637 there are no conversions between the two, so every response would be in a shape {} \
1638 does not define. Mount a version this build can translate, or serve the canonical \
1639 one.",
1640 self.version,
1641 crate::CANONICAL_VERSION,
1642 self.version,
1643 );
1644 let bridging = self.version != crate::CANONICAL_VERSION;
1645 let state = Arc::new(OcpiState {
1646 tokens: self.tokens,
1647 config: self.config,
1648 base_url: self.base_url,
1649 mounted: self.mounted,
1650 version: self.version,
1651 });
1652 let details_state = Arc::clone(&state);
1653 let versions_state = Arc::clone(&state);
1654
1655 let mut router = self
1656 .router
1657 .route(
1658 "/",
1659 get(async move |auth: Auth, ids: Ids| -> Result<_, OcpiErrorResponse> {
1660 let context = context_of(auth, ids, Routing(None), &ModuleId::Versions)?;
1661 let ids = context.ids.clone();
1662 Ok(OcpiReply::ok(details_state.version_details()).with_ids(ids))
1663 }),
1664 )
1665 .route(
1666 "/versions",
1667 get(async move |auth: Auth, ids: Ids| -> Result<_, OcpiErrorResponse> {
1668 let context = context_of(auth, ids, Routing(None), &ModuleId::Versions)?;
1669 let ids = context.ids.clone();
1670 let versions = vec![crate::v2_3_0::versions::Version::new(
1671 versions_state.version.clone(),
1672 versions_state.base_url.clone(),
1673 )];
1674 Ok(OcpiReply::ok(versions).with_ids(ids))
1675 }),
1676 )
1677 .with_state(Arc::clone(&state));
1678 if bridging {
1680 router = router.layer(axum::middleware::from_fn_with_state(state, super::bridge::translate));
1681 }
1682 router
1683 }
1684}
1685
1686fn receiver_path(prefix: Option<&str>, suffix: &str) -> String {
1688 match prefix {
1689 Some(prefix) => format!("/{}{suffix}", prefix.trim_matches('/')),
1690 None => suffix.to_owned(),
1691 }
1692}
1693
1694#[derive(Debug, serde::Deserialize)]
1699struct ActiveProfileQuery {
1700 #[serde(default)]
1701 duration: Option<u64>,
1702 #[serde(default)]
1703 response_url: Option<Url>,
1704}
1705
1706fn partial_object<T>(body: &[u8]) -> Result<crate::transport::Patch<T>, OcpiError> {
1712 let value: serde_json::Value =
1713 serde_json::from_slice(body).map_err(|e| OcpiError::MalformedJson(e.to_string()))?;
1714 if !value.is_object() {
1715 return Err(OcpiError::Decode { path: "/".to_owned(), message: "expected a JSON object".to_owned() });
1716 }
1717 Ok(crate::transport::Patch::from_value(value))
1718}
1719
1720fn missing_query(name: &str) -> OcpiError {
1722 OcpiError::Decode { path: format!("?{name}"), message: format!("the {name} query parameter is required") }
1723}
1724
1725#[derive(Clone, Debug, PartialEq, Eq)]
1753pub struct CallbackUrls {
1754 base: Url,
1755}
1756
1757impl CallbackUrls {
1758 #[must_use]
1760 pub const fn new(base_url: Url) -> Self {
1761 Self { base: base_url }
1762 }
1763
1764 #[must_use]
1766 pub const fn base(&self) -> &Url {
1767 &self.base
1768 }
1769
1770 #[must_use]
1776 pub fn command_result(&self, command: &str, unique_id: &str) -> Url {
1777 self.base.join("commands").join(command).join(unique_id)
1778 }
1779
1780 #[must_use]
1782 pub fn active_charging_profile_result(&self, unique_id: &str) -> Url {
1783 self.base.join("chargingprofiles/result/active").join(unique_id)
1784 }
1785
1786 #[must_use]
1788 pub fn charging_profile_result(&self, unique_id: &str) -> Url {
1789 self.base.join("chargingprofiles/result/set").join(unique_id)
1790 }
1791
1792 #[must_use]
1794 pub fn clear_profile_result(&self, unique_id: &str) -> Url {
1795 self.base.join("chargingprofiles/result/clear").join(unique_id)
1796 }
1797}
1798
1799#[derive(Debug, serde::Deserialize)]
1801struct TokenTypeQuery {
1802 #[serde(rename = "type", default)]
1803 token_type: Option<crate::v2_3_0::tokens::TokenType>,
1804}
1805
1806fn decode_optional_body<T: serde::de::DeserializeOwned>(body: &[u8]) -> Result<Option<T>, OcpiError> {
1810 let trimmed = body.trim_ascii();
1811 if trimmed.is_empty() || trimmed == b"{}" {
1812 return Ok(None);
1813 }
1814 let mut de = serde_json::Deserializer::from_slice(trimmed);
1815 serde_path_to_error::deserialize(&mut de).map(Some).map_err(|e| OcpiError::Decode {
1816 path: format!("/{}", e.path()),
1817 message: e.into_inner().to_string(),
1818 })
1819}
1820
1821fn status_of(created: super::traits::Created) -> http::StatusCode {
1822 http::StatusCode::from_u16(created.http_status()).unwrap_or(http::StatusCode::OK)
1823}
1824
1825fn page_reply<T: crate::types::Validate>(page: Page<T>, state: &Arc<OcpiState>) -> OcpiReply<Vec<T>> {
1826 let mut headers = http::HeaderMap::new();
1827 let meta = PageMeta { limit: Some(state.config.max_page_limit), ..page.meta };
1828 meta.write_to(&mut headers);
1829 OcpiReply::ok(page.items).with_headers(headers)
1830}
1831
1832fn context_of(
1834 Auth(peer): Auth,
1835 Ids(ids): Ids,
1836 Routing(routing): Routing,
1837 module: &ModuleId,
1838) -> Result<RequestContext, OcpiErrorResponse> {
1839 peer.check_scope(module).map_err(|e| OcpiErrorResponse::new(e).with_ids(ids.clone()))?;
1840 Ok(RequestContext { peer, ids, routing })
1841}
1842
1843fn owned_context(
1845 auth: Auth,
1846 ids: Ids,
1847 routing: Routing,
1848 module: &ModuleId,
1849 country_code: &str,
1850 party_id: &str,
1851) -> Result<(RequestContext, PartyRef), OcpiErrorResponse> {
1852 let request_ids = ids.0.clone();
1853 let context = context_of(auth, ids, routing, module)?;
1854 let Owner(owner) = Owner::from_path(country_code, party_id)
1855 .map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids.clone()))?;
1856 context.peer.check_ownership(&owner).map_err(|e| OcpiErrorResponse::new(e).with_ids(request_ids))?;
1857 Ok((context, owner))
1858}
1859
1860fn parse_command(name: &str, body: &[u8]) -> Result<crate::v2_3_0::commands::Command, OcpiError> {
1862 use crate::v2_3_0::commands::{
1863 CancelReservation, Command, CommandType, ReserveNow, StartSession, StopSession, UnlockConnector,
1864 };
1865
1866 fn decode<T: serde::de::DeserializeOwned>(body: &[u8]) -> Result<T, OcpiError> {
1867 let mut de = serde_json::Deserializer::from_slice(body);
1868 serde_path_to_error::deserialize(&mut de).map_err(|e| OcpiError::Decode {
1869 path: format!("/{}", e.path()),
1870 message: e.into_inner().to_string(),
1871 })
1872 }
1873
1874 match CommandType::from(name) {
1875 CommandType::CancelReservation => Ok(Command::CancelReservation(decode::<CancelReservation>(body)?)),
1876 CommandType::ReserveNow => Ok(Command::ReserveNow(Box::new(decode::<ReserveNow>(body)?))),
1877 CommandType::StartSession => Ok(Command::StartSession(Box::new(decode::<StartSession>(body)?))),
1878 CommandType::StopSession => Ok(Command::StopSession(decode::<StopSession>(body)?)),
1879 CommandType::UnlockConnector => Ok(Command::UnlockConnector(decode::<UnlockConnector>(body)?)),
1880 other => Err(OcpiError::NotFound(format!("no such command: {other}"))),
1881 }
1882}
1883
1884#[cfg(test)]
1885mod tests {
1886 use super::*;
1887
1888 #[test]
1889 fn commands_are_decoded_by_the_name_in_the_url() {
1890 let body = br#"{"response_url":"https://msp.example.com/cb/1","session_id":"101"}"#;
1891 let command = parse_command("STOP_SESSION", body).unwrap();
1892 assert_eq!(command.command_type(), crate::v2_3_0::commands::CommandType::StopSession);
1893 assert!(parse_command("nltnm-CUSTOM", body).is_err());
1894 let err = parse_command("STOP_SESSION", b"{}").unwrap_err();
1896 assert_eq!(err.status_code(), crate::transport::StatusCode::INVALID_PARAMETERS);
1897 }
1898
1899 fn router(config: ServerConfig) -> OcpiRouter {
1900 OcpiRouter::new(
1901 VersionNumber::V2_3_0,
1902 Url::new("https://cpo.example.com/ocpi/cpo/2.3.0").expect("a valid URL"),
1903 Arc::new(super::super::InMemoryTokenStore::new()),
1904 )
1905 .with_config(config)
1906 }
1907
1908 #[test]
1914 fn an_ambiguous_pair_is_refused_in_both_mount_orders() {
1915 for module in [ModuleId::Locations, ModuleId::ChargingProfiles, ModuleId::Payments] {
1916 for (first, second) in [
1917 (InterfaceRole::Sender, InterfaceRole::Receiver),
1918 (InterfaceRole::Receiver, InterfaceRole::Sender),
1919 ] {
1920 let mut r = router(ServerConfig::default().one_router_per_role());
1921 r.mounted.add(module.clone(), first);
1922 let refused = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1923 r.check_interface_conflict(&module, second);
1924 }));
1925 assert!(refused.is_err(), "{module} mounted {first} then {second} must be refused");
1926 }
1927 }
1928 }
1929
1930 #[test]
1931 fn a_receiver_path_prefix_is_what_makes_the_pair_servable() {
1932 let mut r = router(ServerConfig::default());
1933 r.mounted.add(ModuleId::Locations, InterfaceRole::Sender);
1934 r.check_interface_conflict(&ModuleId::Locations, InterfaceRole::Receiver);
1936
1937 let mut r = router(ServerConfig::default().one_router_per_role());
1939 r.mounted.add(ModuleId::Tokens, InterfaceRole::Sender);
1940 r.check_interface_conflict(&ModuleId::Tokens, InterfaceRole::Receiver);
1941 }
1942
1943 #[test]
1944 fn every_documented_alternative_is_reachable_from_outside_the_crate() {
1945 let config = ServerConfig::default()
1948 .with_quirks(Quirks::default())
1949 .with_max_page_limit(25)
1950 .with_receiver_path_prefix("emsp");
1951 assert_eq!(config.max_page_limit, 25);
1952 assert_eq!(config.receiver_path_prefix.as_deref(), Some("emsp"));
1953 assert_eq!(ServerConfig::default().one_router_per_role().receiver_path_prefix, None);
1954 }
1955}