headless_lms_server/controllers/
mock_sisu.rs1use crate::prelude::*;
2use headless_lms_utils::services::sisu::{
3 Additional, CourseUnitSearchResults, Credits, Name, Organisation, ResponsibilityInfo,
4 SearchResult, SisuCourseInfoElement, SisuCourseInfoValidityPeriod, SisuPerson,
5};
6use serde_json::json;
7
8const MOCK_COURSE_CODES: [&str; 3] = ["TEST001", "TEST002", "TEST003"];
9const MOCK_TEACHER_ID: &str = "mock-person-teacher";
10const MOCK_REALISATION_TEACHER_ID: &str = "mock-person-realisation-teacher";
11const MOCK_ORGANISATION_ID: &str = "mock-org-programme";
12const MOCK_ASSESSMENT_ITEM_ID: &str = "mock-assessment-item";
13
14#[derive(Deserialize, Debug)]
15#[serde(rename_all = "camelCase")]
16pub struct MockSisuRequest {
17 code_query: String,
18}
19
20async fn mock_sisu_id_query(
22 query_params: web::Query<MockSisuRequest>,
23 app_conf: web::Data<ApplicationConfiguration>,
24) -> ControllerResult<String> {
25 assert!(app_conf.test_mode && app_conf.test_sisu);
26 let code = query_params.code_query.as_str();
27 let search_results = match MOCK_COURSE_CODES.iter().position(|c| *c == code) {
28 Some(index) => (1..=2)
29 .map(|version| SearchResult {
30 id: format!("mock-id-{}-{version}", index + 1),
31 code: Some(code.to_string()),
32 })
33 .collect(),
34 None => vec![],
35 };
36 let res = serde_json::to_string(&CourseUnitSearchResults { search_results })?;
37 let token = skip_authorize();
38 token.authorized_ok(res)
39}
40
41async fn mock_sisu_course_info(
42 app_conf: web::Data<ApplicationConfiguration>,
43 id: web::Path<String>,
44) -> ControllerResult<HttpResponse> {
45 assert!(app_conf.test_mode && app_conf.test_sisu);
46 let token = skip_authorize();
47 let Some((index, version)) = id
48 .strip_prefix("mock-id-")
49 .and_then(|rest| rest.split_once('-'))
50 .and_then(|(index, version)| Some((index.parse::<usize>().ok()?, version)))
51 .filter(|(index, version)| {
52 (1..=MOCK_COURSE_CODES.len()).contains(index) && matches!(*version, "1" | "2")
53 })
54 else {
55 return token.authorized_ok(HttpResponse::NotFound().finish());
56 };
57 let validity_period = if version == "1" {
58 SisuCourseInfoValidityPeriod {
59 start_date: Some("2020-08-01".to_string()),
60 end_date: None,
61 }
62 } else {
63 SisuCourseInfoValidityPeriod {
64 start_date: Some("2015-08-01".to_string()),
65 end_date: Some("2020-08-01".to_string()),
66 }
67 };
68 let course_unit = mock_course_unit(
69 id.as_str(),
70 MOCK_COURSE_CODES[index - 1],
71 &format!("Mock Docker {index}"),
72 validity_period,
73 );
74 token.authorized_ok(HttpResponse::Ok().json(course_unit))
75}
76
77fn mock_course_unit(
78 id: &str,
79 code: &str,
80 name: &str,
81 validity_period: SisuCourseInfoValidityPeriod,
82) -> SisuCourseInfoElement {
83 let english = |text: &str| Additional {
84 en: Some(text.to_string()),
85 fi: None,
86 sv: None,
87 };
88 SisuCourseInfoElement {
89 id: id.to_string(),
90 university_org_ids: vec!["hy-university".to_string()],
91 group_id: "hy-CU-142971304".to_string(),
92 credits: Credits {
93 min: None,
94 max: None,
95 },
96 completion_methods: vec![Some(json!({
97 "studyType": "DEGREE_STUDIES",
98 "automaticEvaluation": true,
99 "assessmentItemIds": [MOCK_ASSESSMENT_ITEM_ID],
100 }))],
101 name: Name {
102 en: Some(name.to_string()),
103 fi: None,
104 sv: None,
105 },
106 code: code.to_string(),
107 abbreviation: None,
108 validity_period,
109 grade_scale_id: "sis-hyl-hyv".to_string(),
110 tweet_text: None,
111 outcomes: Some(english("Mock outcomes")),
112 prerequisites: Some(english("Mock prerequisites")),
113 content: Some(english("Mock content")),
114 additional: Some(english("Mock additional")),
115 learning_material: Some(english("Mock learning materials")),
116 literature: vec![],
117 study_level: "".to_string(),
118 course_unit_type: "".to_string(),
119 subject: None,
120 cefr_level: None,
121 organisations: vec![Organisation {
122 organisation_id: Some(MOCK_ORGANISATION_ID.to_string()),
123 educational_institution_urn: None,
124 role_urn: "urn:code:organisation-role:responsible-organisation".to_string(),
125 share: 1.0,
126 validity_period: None,
127 }],
128 possible_attainment_languages: vec![],
129 part_of_degree: None,
130 responsibility_infos: Some(vec![ResponsibilityInfo {
131 role_urn: "urn:code:module-responsibility-info-type:responsible-teacher".to_string(),
132 person_id: Some(MOCK_TEACHER_ID.to_string()),
133 text: None,
134 validity_period: None,
135 }]),
136 }
137}
138
139async fn mock_sisu_person(
140 app_conf: web::Data<ApplicationConfiguration>,
141 id: web::Path<String>,
142) -> ControllerResult<HttpResponse> {
143 assert!(app_conf.test_mode && app_conf.test_sisu);
144 let token = skip_authorize();
145 let (first_name, last_name, email, title) = match id.as_str() {
146 MOCK_TEACHER_ID => ("Mock", "Teacher", "mock.teacher@example.com", "Professor"),
147 MOCK_REALISATION_TEACHER_ID => (
148 "Mock",
149 "Lecturer",
150 "mock.lecturer@example.com",
151 "University Lecturer",
152 ),
153 _ => return token.authorized_ok(HttpResponse::NotFound().finish()),
154 };
155 token.authorized_ok(HttpResponse::Ok().json(SisuPerson {
156 id: id.into_inner(),
157 first_name: Some(first_name.to_string()),
158 last_name: Some(last_name.to_string()),
159 email_address: Some(email.to_string()),
160 titles: vec![Additional {
161 en: Some(title.to_string()),
162 fi: None,
163 sv: None,
164 }],
165 }))
166}
167
168async fn mock_sisu_organisation(
169 app_conf: web::Data<ApplicationConfiguration>,
170 id: web::Path<String>,
171) -> ControllerResult<HttpResponse> {
172 assert!(app_conf.test_mode && app_conf.test_sisu);
173 let token = skip_authorize();
174 if id.as_str() != MOCK_ORGANISATION_ID {
175 return token.authorized_ok(HttpResponse::NotFound().finish());
176 }
177 token.authorized_ok(HttpResponse::Ok().json(json!({
178 "id": MOCK_ORGANISATION_ID,
179 "name": { "en": "Mock Degree Programme" },
180 })))
181}
182
183async fn mock_sisu_realisations_by_assessment_item(
184 app_conf: web::Data<ApplicationConfiguration>,
185) -> ControllerResult<HttpResponse> {
186 assert!(app_conf.test_mode && app_conf.test_sisu);
187 let token = skip_authorize();
188 token.authorized_ok(HttpResponse::Ok().json(json!([{
189 "id": "mock-realisation",
190 "name": { "en": "Mock implementation" },
191 "activityPeriod": { "startDate": "2020-08-01" },
192 "flowState": "PUBLISHED",
193 "responsibilityInfos": [{
194 "roleUrn": "urn:code:course-unit-realisation-responsibility-info-type:teacher",
195 "personId": MOCK_REALISATION_TEACHER_ID,
196 "text": null,
197 "validityPeriod": {},
198 }],
199 }])))
200}
201
202pub fn _add_routes(cfg: &mut ServiceConfig) {
203 cfg.route("course-unit-search", web::get().to(mock_sisu_id_query))
204 .route("course-units/v1/{id}", web::get().to(mock_sisu_course_info))
205 .route("persons/v1/{id}", web::get().to(mock_sisu_person))
206 .route("organisations/{id}", web::get().to(mock_sisu_organisation))
207 .route(
208 "course-unit-realisations-by-assessment-item-id",
209 web::get().to(mock_sisu_realisations_by_assessment_item),
210 );
211}