1use axum::extract::{Path, State};
5use iota_sdk2::types::{CheckpointSequenceNumber, EpochId, SignedCheckpointSummary};
6use tap::Pipe;
7
8use crate::{
9 RestService, Result,
10 accept::AcceptFormat,
11 openapi::{ApiEndpoint, OperationBuilder, ResponseBuilder, RouteHandler},
12 reader::StateReader,
13 response::ResponseContent,
14};
15
16pub struct GetEpochLastCheckpoint;
17
18impl ApiEndpoint<RestService> for GetEpochLastCheckpoint {
19 fn method(&self) -> axum::http::Method {
20 axum::http::Method::GET
21 }
22
23 fn path(&self) -> &'static str {
24 "/epochs/{epoch}/last-checkpoint"
25 }
26
27 fn operation(
28 &self,
29 generator: &mut schemars::gen::SchemaGenerator,
30 ) -> openapiv3::v3_1::Operation {
31 OperationBuilder::new()
32 .tag("Epochs")
33 .operation_id("GetEpochLastCheckpoint")
34 .path_parameter::<CheckpointSequenceNumber>("checkpoint", generator)
35 .response(
36 200,
37 ResponseBuilder::new()
38 .json_content::<SignedCheckpointSummary>(generator)
39 .bcs_content()
40 .build(),
41 )
42 .response(404, ResponseBuilder::new().build())
43 .build()
44 }
45
46 fn handler(&self) -> RouteHandler<RestService> {
47 RouteHandler::new(self.method(), get_epoch_last_checkpoint)
48 }
49}
50
51async fn get_epoch_last_checkpoint(
52 Path(epoch): Path<EpochId>,
53 accept: AcceptFormat,
54 State(state): State<StateReader>,
55) -> Result<ResponseContent<SignedCheckpointSummary>> {
56 let summary = state
57 .inner()
58 .get_epoch_last_checkpoint(epoch)?
59 .ok_or_else(|| EpochLastCheckpointNotFoundError::new(epoch))?
60 .into_inner()
61 .try_into()?;
62
63 match accept {
64 AcceptFormat::Json => ResponseContent::Json(summary),
65 AcceptFormat::Bcs => ResponseContent::Bcs(summary),
66 }
67 .pipe(Ok)
68}
69
70#[derive(Debug)]
71pub struct EpochLastCheckpointNotFoundError {
72 epoch: EpochId,
73}
74
75impl EpochLastCheckpointNotFoundError {
76 pub fn new(epoch: EpochId) -> Self {
77 Self { epoch }
78 }
79}
80
81impl std::fmt::Display for EpochLastCheckpointNotFoundError {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "Epoch {} last checkpoint not found", self.epoch)
84 }
85}
86
87impl std::error::Error for EpochLastCheckpointNotFoundError {}
88
89impl From<EpochLastCheckpointNotFoundError> for crate::RestError {
90 fn from(value: EpochLastCheckpointNotFoundError) -> Self {
91 Self::new(axum::http::StatusCode::NOT_FOUND, value.to_string())
92 }
93}