headless_lms_credit_registration/runtime/suotar/
course_codes.rs1use headless_lms_utils::services::suotar::{
4 SuotarEndpoint, ValidateCourseCodeRequestItem, endpoints, new_request_item_id,
5};
6use tracing::Instrument;
7
8use super::SuotarStudyRegistry;
9use super::decode::{course_code_verdict, registry_error};
10use super::encode::course_code_item;
11use super::gate::Exchange;
12use crate::registry::{
13 CourseCode, CourseCodeVerdicts, RegistryError, RegistryOperation, StudyRegistry,
14};
15
16const ENDPOINT: SuotarEndpoint = SuotarEndpoint::ValidateCourseCodes;
17
18pub(super) async fn validate(
19 registry: &mut SuotarStudyRegistry<'_>,
20 codes: &[CourseCode],
21) -> Result<CourseCodeVerdicts, RegistryError> {
22 let mut verdicts = CourseCodeVerdicts::new();
23 let mut unchecked = codes;
24 while !unchecked.is_empty() {
25 let allowance = registry.allowance(RegistryOperation::ValidateCourseCodes);
26 if allowance == 0 {
27 break;
28 }
29 let (chunk, rest) = unchecked.split_at(allowance.min(unchecked.len()));
30 unchecked = rest;
31 let items: Vec<ValidateCourseCodeRequestItem> = chunk
32 .iter()
33 .map(|course_code| course_code_item(course_code, new_request_item_id()))
34 .collect();
35 let request_item_ids: Vec<String> = items
36 .iter()
37 .map(|item| item.request_item_id.clone())
38 .collect();
39 registry.gate.spend(ENDPOINT, items.len());
40 let span = super::request_span(ENDPOINT, items.len());
41 let response = registry
42 .client
43 .post::<endpoints::ValidateCourseCodes>(registry.call_context(Vec::new()), items)
44 .instrument(span)
45 .await;
46 let response = match response {
47 Ok(response) => response,
48 Err(error) => {
49 registry.gate.record(ENDPOINT, Exchange::Refused(&error));
50 debug!(
51 checked = verdicts.len(),
52 remaining = unchecked.len(),
53 "Stopped checking Suotar course codes after a refusal"
54 );
55 return Err(registry_error(&error));
56 }
57 };
58 registry.gate.record(
59 ENDPOINT,
60 Exchange::answered(
61 &response,
62 "Every course code of the batch came back unavailable.",
63 ),
64 );
65 for (course_code, request_item_id) in chunk.iter().zip(&request_item_ids) {
66 if let Some(verdict) = response.item(request_item_id).and_then(course_code_verdict) {
67 verdicts.insert(course_code.clone(), verdict);
68 }
69 }
70 }
71 Ok(verdicts)
72}