ns_indexer/api/
service.rs

1use axum::{
2    extract::{Query, State},
3    Extension,
4};
5use std::sync::Arc;
6use validator::Validate;
7
8use ns_axum_web::{
9    context::ReqContext,
10    erring::{HTTPError, SuccessResponse},
11    object::PackObject,
12};
13use ns_protocol::state::ServiceState;
14
15use crate::api::{IndexerAPI, QueryName};
16use crate::db;
17
18pub struct ServiceAPI;
19
20impl ServiceAPI {
21    pub async fn get(
22        State(app): State<Arc<IndexerAPI>>,
23        Extension(ctx): Extension<Arc<ReqContext>>,
24        to: PackObject<()>,
25        input: Query<QueryName>,
26    ) -> Result<PackObject<SuccessResponse<ServiceState>>, HTTPError> {
27        input.validate()?;
28        if input.code.is_none() {
29            return Err(HTTPError::new(400, "service code is required".to_string()));
30        }
31
32        let name = input.name.clone();
33        let code = input.code.unwrap();
34        ctx.set_kvs(vec![
35            ("action", "get_service_state".into()),
36            ("name", name.clone().into()),
37            ("code", code.into()),
38        ])
39        .await;
40
41        let mut service_state = db::ServiceState::with_pk(name, code);
42        service_state.get_one(&app.scylla, vec![]).await?;
43
44        Ok(to.with(SuccessResponse::new(service_state.to_index()?)))
45    }
46
47    pub async fn get_best(
48        State(app): State<Arc<IndexerAPI>>,
49        Extension(ctx): Extension<Arc<ReqContext>>,
50        to: PackObject<()>,
51        input: Query<QueryName>,
52    ) -> Result<PackObject<SuccessResponse<ServiceState>>, HTTPError> {
53        input.validate()?;
54        if input.code.is_none() {
55            return Err(HTTPError::new(400, "service code is required".to_string()));
56        }
57
58        let name = input.name.clone();
59        let code = input.code.unwrap() as u64;
60        ctx.set_kvs(vec![
61            ("action", "get_best_service_state".into()),
62            ("name", name.clone().into()),
63            ("code", code.into()),
64        ])
65        .await;
66
67        {
68            let best_names_state = app.state.confirming_names.read().await;
69            if let Some(states) = best_names_state.get(&name) {
70                for ss in states.iter().rev() {
71                    if ss.1.code == code {
72                        return Ok(to.with(SuccessResponse::new(ss.1.clone())));
73                    }
74                }
75            }
76        }
77
78        Err(HTTPError::new(404, "not found".to_string()))
79    }
80
81    pub async fn list_by_name(
82        State(app): State<Arc<IndexerAPI>>,
83        Extension(ctx): Extension<Arc<ReqContext>>,
84        to: PackObject<()>,
85        input: Query<QueryName>,
86    ) -> Result<PackObject<SuccessResponse<Vec<ServiceState>>>, HTTPError> {
87        input.validate()?;
88
89        let name = input.name.clone();
90        ctx.set_kvs(vec![
91            ("action", "list_service_states_by_name".into()),
92            ("name", name.clone().into()),
93        ])
94        .await;
95
96        let res = db::ServiceState::list_by_name(&app.scylla, &name, vec![]).await?;
97        let mut service_states: Vec<ServiceState> = Vec::with_capacity(res.len());
98        for i in res {
99            service_states.push(i.to_index()?);
100        }
101        Ok(to.with(SuccessResponse::new(service_states)))
102    }
103}