Skip to main content

headless_lms_server/controllers/
mock_sisu.rs

1use 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
20/// Each mock code has a current version (`-1`) and an ended one (`-2`).
21async 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}