1
use std::{collections::HashMap, error::Error as StdError, sync::Arc};
2

            
3
use actix_web::{dev::HttpServiceFactory, error, web, HttpResponse, Responder};
4
use reqwest;
5
use serde::{Deserialize, Serialize};
6

            
7
use general_mq::Queue;
8
use sylvia_iot_corelib::{constants::DbEngine, err::ErrResp};
9

            
10
use crate::{
11
    libs::{
12
        config::{self, Config},
13
        mq::{self, Connection},
14
    },
15
    models::{self, ConnOptions, Model, MongoDbOptions, SqliteOptions},
16
};
17

            
18
pub mod middleware;
19
mod v1;
20

            
21
/// The resources used by this service.
22
#[derive(Clone)]
23
pub struct State {
24
    /// The scope root path for the service.
25
    ///
26
    /// For example `/data`, the APIs are
27
    /// - `http://host:port/data/api/v1/application-uldata/xxx`
28
    /// - `http://host:port/data/api/v1/network-uldata/xxx`
29
    pub scope_path: &'static str,
30
    /// The database model.
31
    pub model: Arc<dyn Model>,
32
    /// The sylvia-iot-auth base API path with host.
33
    ///
34
    /// For example, `http://localhost:1080/auth`.
35
    pub auth_base: String,
36
    /// The sylvia-iot-broker base API path with host.
37
    ///
38
    /// For example, `http://localhost:2080/broker`.
39
    pub broker_base: String,
40
    /// The client for internal HTTP requests.
41
    pub client: reqwest::Client,
42
    /// Queue connections. Key is uri.
43
    pub mq_conns: HashMap<String, Connection>,
44
    /// Data channel receivers. Key is data channel name such as `broker.data`, `coremgr.data`, ...
45
    pub data_receivers: HashMap<String, Queue>,
46
}
47

            
48
/// The sylvia-iot module specific error codes in addition to standard [`ErrResp`].
49
pub struct ErrReq;
50

            
51
/// Query parameters for `GET /version`
52
10
#[derive(Deserialize)]
53
pub struct GetVersionQuery {
54
    q: Option<String>,
55
}
56

            
57
#[derive(Serialize)]
58
struct GetVersionRes<'a> {
59
    data: GetVersionResData<'a>,
60
}
61

            
62
#[derive(Serialize)]
63
struct GetVersionResData<'a> {
64
    name: &'a str,
65
    version: &'a str,
66
}
67

            
68
const SERV_NAME: &'static str = env!("CARGO_PKG_NAME");
69
const SERV_VER: &'static str = env!("CARGO_PKG_VERSION");
70

            
71
impl ErrReq {
72
    pub const UNIT_NOT_EXIST: (u16, &'static str) = (400, "err_data_unit_not_exist");
73
    pub const USER_NOT_EXIST: (u16, &'static str) = (400, "err_data_user_not_exist");
74
}
75

            
76
/// To create resources for the service.
77
8
pub async fn new_state(
78
8
    scope_path: &'static str,
79
8
    conf: &Config,
80
8
) -> Result<State, Box<dyn StdError>> {
81
8
    let conf = config::apply_default(conf);
82
8
    let db_opts = match conf.db.as_ref().unwrap().engine.as_ref().unwrap().as_str() {
83
8
        DbEngine::MONGODB => {
84
3
            let conf = conf.db.as_ref().unwrap().mongodb.as_ref().unwrap();
85
3
            ConnOptions::MongoDB(MongoDbOptions {
86
3
                url: conf.url.as_ref().unwrap().to_string(),
87
3
                db: conf.database.as_ref().unwrap().to_string(),
88
3
                pool_size: conf.pool_size,
89
3
            })
90
        }
91
        _ => {
92
5
            let conf = conf.db.as_ref().unwrap().sqlite.as_ref().unwrap();
93
5
            ConnOptions::Sqlite(SqliteOptions {
94
5
                path: conf.path.as_ref().unwrap().to_string(),
95
5
            })
96
        }
97
    };
98
199
    let model = models::new(&db_opts).await?;
99
8
    let auth_base = conf.auth.as_ref().unwrap().clone();
100
8
    let broker_base = conf.broker.as_ref().unwrap().clone();
101
8
    let mut mq_conns = HashMap::new();
102
8
    let ch_conf = conf.mq_channels.as_ref().unwrap();
103
8
    let data_receivers = new_data_receivers(&model, &mut mq_conns, ch_conf)?;
104
8
    let state = State {
105
8
        scope_path,
106
8
        model,
107
8
        auth_base,
108
8
        broker_base,
109
8
        client: reqwest::Client::new(),
110
8
        mq_conns,
111
8
        data_receivers,
112
8
    };
113
8
    Ok(state)
114
8
}
115

            
116
/// To register service URIs in the specified root path.
117
1263
pub fn new_service(state: &State) -> impl HttpServiceFactory {
118
1263
    web::scope(state.scope_path)
119
1263
        .app_data(web::JsonConfig::default().error_handler(|err, _| {
120
            error::ErrorBadRequest(ErrResp::ErrParam(Some(err.to_string())))
121
1263
        }))
122
1263
        .app_data(web::QueryConfig::default().error_handler(|err, _| {
123
60
            error::ErrorBadRequest(ErrResp::ErrParam(Some(err.to_string())))
124
1263
        }))
125
1263
        .app_data(web::Data::new(state.clone()))
126
1263
        .service(v1::application_uldata::new_service(
127
1263
            "/api/v1/application-uldata",
128
1263
            state,
129
1263
        ))
130
1263
        .service(v1::application_dldata::new_service(
131
1263
            "/api/v1/application-dldata",
132
1263
            state,
133
1263
        ))
134
1263
        .service(v1::network_uldata::new_service(
135
1263
            "/api/v1/network-uldata",
136
1263
            state,
137
1263
        ))
138
1263
        .service(v1::network_dldata::new_service(
139
1263
            "/api/v1/network-dldata",
140
1263
            state,
141
1263
        ))
142
1263
        .service(v1::coremgr_opdata::new_service(
143
1263
            "/api/v1/coremgr-opdata",
144
1263
            state,
145
1263
        ))
146
1263
}
147

            
148
8
pub fn new_data_receivers(
149
8
    model: &Arc<dyn Model>,
150
8
    mq_conns: &mut HashMap<String, Connection>,
151
8
    ch_conf: &config::MqChannels,
152
8
) -> Result<HashMap<String, Queue>, Box<dyn StdError>> {
153
8
    let mut data_receivers = HashMap::<String, Queue>::new();
154
8

            
155
8
    let conf = ch_conf.broker.as_ref().unwrap();
156
8
    let q = mq::broker::new(model.clone(), mq_conns, &conf)?;
157
8
    data_receivers.insert("broker.data".to_string(), q);
158
8

            
159
8
    let conf = ch_conf.coremgr.as_ref().unwrap();
160
8
    let q = mq::coremgr::new(model.clone(), mq_conns, &conf)?;
161
8
    data_receivers.insert("coremgr.data".to_string(), q);
162
8

            
163
8
    Ok(data_receivers)
164
8
}
165

            
166
4
pub async fn get_version(query: web::Query<GetVersionQuery>) -> impl Responder {
167
4
    if let Some(q) = query.q.as_ref() {
168
3
        match q.as_str() {
169
3
            "name" => return HttpResponse::Ok().body(SERV_NAME),
170
2
            "version" => return HttpResponse::Ok().body(SERV_VER),
171
1
            _ => (),
172
        }
173
1
    }
174

            
175
2
    HttpResponse::Ok().json(GetVersionRes {
176
2
        data: GetVersionResData {
177
2
            name: SERV_NAME,
178
2
            version: SERV_VER,
179
2
        },
180
2
    })
181
4
}