ns-indexer 0.6.0

Name & Service Protocol indexer service in Rust
Documentation
use axum::{
    extract::{Query, State},
    Extension,
};
use std::sync::Arc;
use validator::Validate;

use ns_axum_web::{
    context::ReqContext,
    erring::{HTTPError, SuccessResponse},
    object::PackObject,
};
use ns_protocol::{ns, state::NameState};

use crate::api::{IndexerAPI, QueryName, QueryPubkey};
use crate::db;

pub struct NameAPI;

impl NameAPI {
    pub async fn get(
        State(app): State<Arc<IndexerAPI>>,
        Extension(ctx): Extension<Arc<ReqContext>>,
        to: PackObject<()>,
        input: Query<QueryName>,
    ) -> Result<PackObject<SuccessResponse<NameState>>, HTTPError> {
        input.validate()?;

        let name = input.name.clone();
        ctx.set_kvs(vec![
            ("action", "get_name_state".into()),
            ("name", name.clone().into()),
        ])
        .await;

        let mut name_state = db::NameState::with_pk(name);
        name_state.get_one(&app.scylla, vec![]).await?;

        Ok(to.with(SuccessResponse::new(name_state.to_index()?)))
    }

    pub async fn get_best(
        State(app): State<Arc<IndexerAPI>>,
        Extension(ctx): Extension<Arc<ReqContext>>,
        to: PackObject<()>,
        input: Query<QueryName>,
    ) -> Result<PackObject<SuccessResponse<NameState>>, HTTPError> {
        input.validate()?;

        let name = input.name.clone();
        ctx.set_kvs(vec![
            ("action", "get_best_name_state".into()),
            ("name", name.clone().into()),
        ])
        .await;

        {
            let best_names_state = app.state.confirming_names.read().await;
            if let Some(states) = best_names_state.get(&name) {
                if let Some(state) = states.back() {
                    return Ok(to.with(SuccessResponse::new(state.0.clone())));
                }
            }
        }

        Err(HTTPError::new(404, "not found".to_string()))
    }

    pub async fn list_best_by_query(
        State(app): State<Arc<IndexerAPI>>,
        Extension(ctx): Extension<Arc<ReqContext>>,
        to: PackObject<()>,
        input: Query<QueryName>,
    ) -> Result<PackObject<SuccessResponse<Vec<String>>>, HTTPError> {
        input.validate()?;

        let query = input.name.clone();
        ctx.set_kvs(vec![
            ("action", "list_best_names_by_query".into()),
            ("query", query.clone().into()),
        ])
        .await;

        let mut names: Vec<String> = Vec::new();

        {
            let best_names_state = app.state.confirming_names.read().await;
            for n in best_names_state.keys() {
                if n.starts_with(&query) {
                    names.push(n.clone());
                }
            }
        }

        Ok(to.with(SuccessResponse::new(names)))
    }

    pub async fn list_by_query(
        State(app): State<Arc<IndexerAPI>>,
        Extension(ctx): Extension<Arc<ReqContext>>,
        to: PackObject<()>,
        input: Query<QueryName>,
    ) -> Result<PackObject<SuccessResponse<Vec<String>>>, HTTPError> {
        input.validate()?;

        let query = input.name.clone();
        ctx.set_kvs(vec![
            ("action", "list_names_by_query".into()),
            ("query", query.clone().into()),
        ])
        .await;

        let names = db::NameState::list_by_query(&app.scylla, query).await?;

        Ok(to.with(SuccessResponse::new(names)))
    }

    pub async fn list_by_pubkey(
        State(app): State<Arc<IndexerAPI>>,
        Extension(ctx): Extension<Arc<ReqContext>>,
        to: PackObject<()>,
        input: Query<QueryPubkey>,
    ) -> Result<PackObject<SuccessResponse<Vec<String>>>, HTTPError> {
        input.validate()?;

        let key = if input.pubkey.starts_with("0x") {
            &input.pubkey[2..]
        } else {
            input.pubkey.as_str()
        };
        let pubkey = hex::decode(key)
            .map_err(|_| HTTPError::new(400, format!("Invalid pubkey: {}", input.pubkey)))?;
        let pubkey =
            ns::Bytes32::try_from(&pubkey).map_err(|e| HTTPError::new(400, e.to_string()))?;
        ctx.set_kvs(vec![("action", "list_names_by_pubkey".into())])
            .await;

        let mut names = db::NameState::list_by_pubkey(&app.scylla, pubkey).await?;
        names.sort();
        Ok(to.with(SuccessResponse::new(names)))
    }
}