instrumentality/routes/
add.rs

1//! Route for adding data to Instrumentality.
2//!
3//! The /add route is implemented here.
4//!
5//! See endpoint documentation at
6//! <https://docs.berserksystems.com/endpoints/add/>.
7//!
8//! See [`Data`] for examples of valid data objects.
9
10use axum::Extension;
11use axum::{http::StatusCode, response::IntoResponse, Json};
12use mongodb::bson::doc;
13use mongodb::Collection;
14
15use crate::config::IConfig;
16use crate::data::{Data, Datas};
17use crate::database::DBHandle;
18use crate::routes::queue;
19use crate::routes::queue::InternalQueueItem;
20use crate::routes::response::{ErrorResponse, OkResponse};
21use crate::user::User;
22
23pub async fn add(
24    user: User,
25    mut db: DBHandle,
26    Extension(config): Extension<IConfig>,
27    Json(datas): Json<Datas>,
28) -> impl IntoResponse {
29    if datas.data.is_empty() {
30        return error!(BAD_REQUEST, "No data was submitted.");
31    }
32
33    if let Some(queue_id) = datas.queue_id.as_ref() {
34        if get_queue_item(queue_id, &user, &mut db).await.is_none() {
35            return error!(BAD_REQUEST, "Invalid queue ID.");
36        }
37    }
38
39    match process(datas, &config, &user, &mut db).await {
40        true => ok!(CREATED),
41        false => error!(
42            BAD_REQUEST,
43            "No valid data was submitted. Ensure the given platforms and 
44                content/presence types are supported by this server. Ensure all 
45                data was correctly labeled for queue jobs."
46        ),
47    }
48}
49
50async fn get_queue_item(
51    queue_id: &str,
52    user: &User,
53    db: &mut DBHandle,
54) -> Option<InternalQueueItem> {
55    let q_coll: Collection<InternalQueueItem> = db.collection("queue");
56    let queue_item = q_coll
57        .find_one_with_session(
58            doc! {"queue_id": &queue_id, "lock_holder": &user.uuid },
59            None,
60            &mut db.session,
61        )
62        .await
63        .unwrap();
64
65    queue_item
66}
67
68// The logic for this function needs to be simplified.
69// This function resolves a number of uncertainties:
70// - Is there any data remaining after verifying against the config?
71// - If there is a valid queue ID, if there is a valid queue item for that queue
72//   ID, is there any data remaining after verifying against the queue item?
73// - Does the data succeed in verifying against the queue?
74// And then it adds the data if all the answers are yes.
75async fn process(
76    datas: Datas,
77    config: &IConfig,
78    user: &User,
79    db: &mut DBHandle,
80) -> bool {
81    let data_coll: Collection<Data> = db.collection("data");
82
83    let datas = datas.tag(&user.uuid).verify_for_config(config);
84
85    if datas.data.is_empty() {
86        return false;
87    }
88
89    if let Some(queue_id) = &datas.queue_id.clone() {
90        let queue_item = get_queue_item(queue_id, user, db).await;
91
92        if queue_item.is_none() {
93            return false;
94        }
95
96        let queue_item = queue_item.unwrap();
97        let datas = datas.verify_for_queue(queue_item);
98
99        if datas.data.is_empty() {
100            return false;
101        }
102
103        let (platform_id, platform, added_by, username) = datas.info();
104
105        let process_success = queue::process(
106            queue_id,
107            &platform_id,
108            &platform,
109            &added_by,
110            username,
111            db,
112        )
113        .await;
114
115        if !process_success {
116            false
117        } else {
118            data_coll
119                .insert_many_with_session(datas.data, None, &mut db.session)
120                .await
121                .unwrap();
122            db.session.commit_transaction().await.is_ok()
123        }
124    } else {
125        data_coll
126            .insert_many_with_session(datas.data, None, &mut db.session)
127            .await
128            .unwrap();
129        db.session.commit_transaction().await.is_ok()
130    }
131}