mcaptcha/api/v1/
survey.rs1use actix_web::web::ServiceConfig;
18use actix_web::{web, HttpResponse, Responder};
19use serde::{Deserialize, Serialize};
20
21use crate::errors::*;
22use crate::AppData;
23
24pub fn services(cfg: &mut ServiceConfig) {
25 cfg.service(download);
26 cfg.service(secret);
27}
28
29pub mod routes {
30 pub struct Survey {
31 pub download: &'static str,
32 pub secret: &'static str,
33 }
34
35 impl Survey {
36 pub const fn new() -> Self {
37 Self {
38 download: "/api/v1/survey/takeout/{survey_id}/get",
39 secret: "/api/v1/survey/secret",
40 }
41 }
42
43 pub fn get_download_route(&self, survey_id: &str, page: usize) -> String {
44 format!(
45 "{}?page={}",
46 self.download.replace("{survey_id}", survey_id),
47 page
48 )
49 }
50 }
51}
52
53#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
54pub struct Page {
55 pub page: usize,
56}
57
58#[my_codegen::get(path = "crate::V1_API_ROUTES.survey.download")]
60async fn download(
61 data: AppData,
62 page: web::Query<Page>,
63 psuedo_id: web::Path<uuid::Uuid>,
64) -> ServiceResult<impl Responder> {
65 const LIMIT: usize = 50;
66 let offset = LIMIT as isize * ((page.page as isize) - 1);
67 let offset = if offset < 0 { 0 } else { offset };
68 let psuedo_id = psuedo_id.into_inner();
69 let campaign_id = data
70 .db
71 .analytics_get_capmaign_id_from_psuedo_id(&psuedo_id.to_string())
72 .await?;
73 let data = data
74 .db
75 .analytics_fetch(&campaign_id, LIMIT, offset as usize)
76 .await?;
77 Ok(HttpResponse::Ok().json(data))
78}
79
80#[derive(Serialize, Deserialize)]
81struct SurveySecretUpload {
82 secret: String,
83 auth_token: String,
84}
85
86#[my_codegen::post(path = "crate::V1_API_ROUTES.survey.secret")]
88async fn secret(
89 data: AppData,
90 payload: web::Json<SurveySecretUpload>,
91) -> ServiceResult<impl Responder> {
92 match data.survey_secrets.get(&payload.auth_token) {
93 Some(survey_instance_url) => {
94 let payload = payload.into_inner();
95 data.survey_secrets.set(survey_instance_url, payload.secret);
96 data.survey_secrets.rm(&payload.auth_token);
97 Ok(HttpResponse::Ok())
98 }
99 None => Err(ServiceError::WrongPassword),
100 }
101}
102
103#[cfg(test)]
104pub mod tests {
105 use actix_web::{http::StatusCode, test, App};
106
107 use super::*;
108 use crate::api::v1::mcaptcha::get_random;
109 use crate::tests::*;
110 use crate::*;
111
112 #[actix_rt::test]
113 async fn survey_works_pg() {
114 let data = crate::tests::pg::get_data().await;
115 survey_registration_works(data.clone()).await;
116 survey_works(data).await;
117 }
118
119 #[actix_rt::test]
120 async fn survey_works_maria() {
121 let data = crate::tests::maria::get_data().await;
122 survey_registration_works(data.clone()).await;
123 survey_works(data).await;
124 }
125
126 pub async fn survey_registration_works(data: ArcData) {
127 let data = &data;
128 let app = get_app!(data).await;
129
130 let survey_instance_url = "http://survey_registration_works.survey.example.org";
131
132 let key = get_random(20);
133
134 let msg = SurveySecretUpload {
135 auth_token: key.clone(),
136 secret: get_random(32),
137 };
138
139 bad_post_req_test_no_auth(
142 data,
143 V1_API_ROUTES.survey.secret,
144 &msg,
145 errors::ServiceError::WrongPassword,
146 )
147 .await;
148
149 data.survey_secrets
151 .set(key.clone(), survey_instance_url.to_owned());
152 let resp = test::call_service(
153 &app,
154 post_request!(&msg, V1_API_ROUTES.survey.secret).to_request(),
155 )
156 .await;
157 assert_eq!(resp.status(), StatusCode::OK);
158 assert_eq!(
160 data.survey_secrets.get(survey_instance_url).unwrap(),
161 msg.secret
162 );
163
164 bad_post_req_test_no_auth(
166 data,
167 V1_API_ROUTES.survey.secret,
168 &msg,
169 errors::ServiceError::WrongPassword,
170 )
171 .await;
172 }
173
174 pub async fn survey_works(data: ArcData) {
175 const NAME: &str = "survetuseranalytics";
176 const PASSWORD: &str = "longpassworddomain";
177 const EMAIL: &str = "survetuseranalytics@a.com";
178 let data = &data;
179
180 delete_user(data, NAME).await;
181
182 register_and_signin(data, NAME, EMAIL, PASSWORD).await;
183 let (_, _signin_resp, key) = add_levels_util(data, NAME, PASSWORD).await;
185 let app = get_app!(data).await;
186
187 let page = 1;
188 let tmp_id = uuid::Uuid::new_v4();
189 let download_rotue = V1_API_ROUTES
190 .survey
191 .get_download_route(&tmp_id.to_string(), page);
192
193 let download_req = test::call_service(
194 &app,
195 test::TestRequest::get().uri(&download_rotue).to_request(),
196 )
197 .await;
198 assert_eq!(download_req.status(), StatusCode::NOT_FOUND);
199
200 data.db
201 .analytics_create_psuedo_id_if_not_exists(&key.key)
202 .await
203 .unwrap();
204
205 let psuedo_id = data
206 .db
207 .analytics_get_psuedo_id_from_capmaign_id(&key.key)
208 .await
209 .unwrap();
210
211 for i in 0..60 {
212 println!("[{i}] Saving analytics");
213 let analytics = db_core::CreatePerformanceAnalytics {
214 time: 0,
215 difficulty_factor: 0,
216 worker_type: "wasm".into(),
217 };
218 data.db.analysis_save(&key.key, &analytics).await.unwrap();
219 }
220
221 for p in 1..3 {
222 let download_rotue = V1_API_ROUTES.survey.get_download_route(&psuedo_id, p);
223 println!("page={p}, download={download_rotue}");
224
225 let download_req = test::call_service(
226 &app,
227 test::TestRequest::get().uri(&download_rotue).to_request(),
228 )
229 .await;
230 assert_eq!(download_req.status(), StatusCode::OK);
231 let analytics: Vec<db_core::PerformanceAnalytics> =
232 test::read_body_json(download_req).await;
233 if p == 1 {
234 assert_eq!(analytics.len(), 50);
235 } else if p == 2 {
236 assert_eq!(analytics.len(), 10);
237 } else {
238 assert_eq!(analytics.len(), 0);
239 }
240 }
241
242 let download_rotue = V1_API_ROUTES.survey.get_download_route(&psuedo_id, 0);
243 data.db
244 .analytics_delete_all_records_for_campaign(&key.key)
245 .await
246 .unwrap();
247
248 let download_req = test::call_service(
249 &app,
250 test::TestRequest::get().uri(&download_rotue).to_request(),
251 )
252 .await;
253 assert_eq!(download_req.status(), StatusCode::NOT_FOUND);
254 }
255}