gRPC
The server includes a gRPC client that fetches assessment algorithm bundles from the Go diagnostics backend. This data powers the adaptive algorithm in student assessments.
BundleFetcher Trait
Section titled “BundleFetcher Trait”Pluggable interface for bundle sources:
#[async_trait]pub trait BundleFetcher: Send + Sync + 'static { async fn fetch( &self, network_index: i32, curriculum_index: i32, ) -> Result<Vec<u8>, BundleFetchError>;}GrpcBundleFetcher
Section titled “GrpcBundleFetcher”Production implementation connecting to the Go diagnostics service:
pub struct GrpcBundleFetcher { endpoint: String, // e.g. "http://diagnostics:50051"}Uses hand-written proto types (no protoc/build.rs required):
// Requestpub struct GetAlgorithmBundleRequest { pub network_index: i32, pub curriculum_index: i32,}
// Responsepub struct GetAlgorithmBundleResponse { pub data: Vec<u8>, // JSON-encoded AlgorithmBundle}The hand-written tonic client matches the Go server’s service definition:
impl GrpcBundleFetcher { pub async fn fetch(&self, network_index: i32, curriculum_index: i32) -> Result<Vec<u8>, BundleFetchError> { let mut client = DiagnosticsClient::connect(self.endpoint.clone()).await?; let response = client.get_algorithm_bundle(GetAlgorithmBundleRequest { network_index, curriculum_index, }).await?; Ok(response.into_inner().data) }}NoopBundleFetcher
Section titled “NoopBundleFetcher”Development fallback when the Go backend is unavailable:
pub struct NoopBundleFetcher;
impl BundleFetcher for NoopBundleFetcher { async fn fetch(&self, _, _) -> Result<Vec<u8>, BundleFetchError> { Err(BundleFetchError::Transport("noop fetcher".into())) }}Used when DIAGNOSTICS_GRPC_URL is not set.
Error Types
Section titled “Error Types”pub enum BundleFetchError { Transport(String), Status(tonic::Status),}Data Flow
Section titled “Data Flow”graph LR GQL[GraphQL Mutation] -->|refreshAssessmentBundle| Fetcher[GrpcBundleFetcher] Fetcher -->|gRPC| Go[Go Diagnostics :50051] Go --> Fetcher Fetcher --> Repo[AssessmentBundleRepository] Repo --> PG[(PostgreSQL)] PG -->|assessmentBundle query| Client[Rust Client] Client -->|AlgorithmBundle::into_engine| Engine[AlgorithmEngine]- Refresh — GraphQL mutation triggers gRPC fetch from diagnostics service
- Store — Response bytes (JSON
AlgorithmBundle) are upserted into PostgreSQL - Load — Client queries the bundle via GraphQL
- Build —
AlgorithmBundle::from_json()→into_engine()builds theAlgorithmEngine