sylvia_iot_data/routes/
mod.rs

1use std::{collections::HashMap, error::Error as StdError, sync::Arc};
2
3use axum::{response::IntoResponse, Router};
4use reqwest;
5use serde::{Deserialize, Serialize};
6
7use general_mq::Queue;
8use sylvia_iot_corelib::{
9    constants::DbEngine,
10    http::{Json, Query},
11};
12
13use crate::{
14    libs::{
15        config::{self, Config},
16        mq::{self, Connection},
17    },
18    models::{self, ConnOptions, Model, MongoDbOptions, SqliteOptions},
19};
20
21pub mod middleware;
22mod v1;
23
24/// The resources used by this service.
25#[derive(Clone)]
26pub struct State {
27    /// The scope root path for the service.
28    ///
29    /// For example `/data`, the APIs are
30    /// - `http://host:port/data/api/v1/application-uldata/xxx`
31    /// - `http://host:port/data/api/v1/network-uldata/xxx`
32    pub scope_path: &'static str,
33    /// The database model.
34    pub model: Arc<dyn Model>,
35    /// The sylvia-iot-auth base API path with host.
36    ///
37    /// For example, `http://localhost:1080/auth`.
38    pub auth_base: String,
39    /// The sylvia-iot-broker base API path with host.
40    ///
41    /// For example, `http://localhost:2080/broker`.
42    pub broker_base: String,
43    /// The client for internal HTTP requests.
44    pub client: reqwest::Client,
45    /// Queue connections. Key is uri.
46    pub mq_conns: HashMap<String, Connection>,
47    /// Data channel receivers. Key is data channel name such as `broker.data`, `coremgr.data`, ...
48    pub data_receivers: HashMap<String, Queue>,
49}
50
51/// The sylvia-iot module specific error codes in addition to standard
52/// [`sylvia_iot_corelib::err::ErrResp`].
53pub struct ErrReq;
54
55/// Query parameters for `GET /version`
56#[derive(Deserialize)]
57pub struct GetVersionQuery {
58    q: Option<String>,
59}
60
61#[derive(Serialize)]
62struct GetVersionRes<'a> {
63    data: GetVersionResData<'a>,
64}
65
66#[derive(Serialize)]
67struct GetVersionResData<'a> {
68    name: &'a str,
69    version: &'a str,
70}
71
72const SERV_NAME: &'static str = env!("CARGO_PKG_NAME");
73const SERV_VER: &'static str = env!("CARGO_PKG_VERSION");
74
75impl ErrReq {
76    pub const UNIT_NOT_EXIST: (u16, &'static str) = (400, "err_data_unit_not_exist");
77    pub const USER_NOT_EXIST: (u16, &'static str) = (400, "err_data_user_not_exist");
78}
79
80/// To create resources for the service.
81pub async fn new_state(
82    scope_path: &'static str,
83    conf: &Config,
84) -> Result<State, Box<dyn StdError>> {
85    let conf = config::apply_default(conf);
86    let db_opts = match conf.db.as_ref().unwrap().engine.as_ref().unwrap().as_str() {
87        DbEngine::MONGODB => {
88            let conf = conf.db.as_ref().unwrap().mongodb.as_ref().unwrap();
89            ConnOptions::MongoDB(MongoDbOptions {
90                url: conf.url.as_ref().unwrap().to_string(),
91                db: conf.database.as_ref().unwrap().to_string(),
92                pool_size: conf.pool_size,
93            })
94        }
95        _ => {
96            let conf = conf.db.as_ref().unwrap().sqlite.as_ref().unwrap();
97            ConnOptions::Sqlite(SqliteOptions {
98                path: conf.path.as_ref().unwrap().to_string(),
99            })
100        }
101    };
102    let model = models::new(&db_opts).await?;
103    let auth_base = conf.auth.as_ref().unwrap().clone();
104    let broker_base = conf.broker.as_ref().unwrap().clone();
105    let mut mq_conns = HashMap::new();
106    let ch_conf = conf.mq_channels.as_ref().unwrap();
107    let data_receivers = new_data_receivers(&model, &mut mq_conns, ch_conf)?;
108    let state = State {
109        scope_path,
110        model,
111        auth_base,
112        broker_base,
113        client: reqwest::Client::new(),
114        mq_conns,
115        data_receivers,
116    };
117    Ok(state)
118}
119
120/// To register service URIs in the specified root path.
121pub fn new_service(state: &State) -> Router {
122    Router::new().nest(
123        &state.scope_path,
124        Router::new()
125            .merge(v1::application_uldata::new_service(
126                "/api/v1/application-uldata",
127                state,
128            ))
129            .merge(v1::application_dldata::new_service(
130                "/api/v1/application-dldata",
131                state,
132            ))
133            .merge(v1::network_uldata::new_service(
134                "/api/v1/network-uldata",
135                state,
136            ))
137            .merge(v1::network_dldata::new_service(
138                "/api/v1/network-dldata",
139                state,
140            ))
141            .merge(v1::coremgr_opdata::new_service(
142                "/api/v1/coremgr-opdata",
143                state,
144            )),
145    )
146}
147
148pub fn new_data_receivers(
149    model: &Arc<dyn Model>,
150    mq_conns: &mut HashMap<String, Connection>,
151    ch_conf: &config::MqChannels,
152) -> Result<HashMap<String, Queue>, Box<dyn StdError>> {
153    let mut data_receivers = HashMap::<String, Queue>::new();
154
155    let conf = ch_conf.broker.as_ref().unwrap();
156    let q = mq::broker::new(model.clone(), mq_conns, &conf)?;
157    data_receivers.insert("broker.data".to_string(), q);
158
159    let conf = ch_conf.coremgr.as_ref().unwrap();
160    let q = mq::coremgr::new(model.clone(), mq_conns, &conf)?;
161    data_receivers.insert("coremgr.data".to_string(), q);
162
163    Ok(data_receivers)
164}
165
166pub async fn get_version(Query(query): Query<GetVersionQuery>) -> impl IntoResponse {
167    if let Some(q) = query.q.as_ref() {
168        match q.as_str() {
169            "name" => return SERV_NAME.into_response(),
170            "version" => return SERV_VER.into_response(),
171            _ => (),
172        }
173    }
174
175    Json(GetVersionRes {
176        data: GetVersionResData {
177            name: SERV_NAME,
178            version: SERV_VER,
179        },
180    })
181    .into_response()
182}