Skip to content

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.

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>;
}

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):

// Request
pub struct GetAlgorithmBundleRequest {
pub network_index: i32,
pub curriculum_index: i32,
}
// Response
pub 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)
}
}

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.

pub enum BundleFetchError {
Transport(String),
Status(tonic::Status),
}
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]
  1. Refresh — GraphQL mutation triggers gRPC fetch from diagnostics service
  2. Store — Response bytes (JSON AlgorithmBundle) are upserted into PostgreSQL
  3. Load — Client queries the bundle via GraphQL
  4. BuildAlgorithmBundle::from_json()into_engine() builds the AlgorithmEngine
  • Algorithm — how the engine uses bundle data
  • BundlesAssessmentBundleRepository storage
  • GraphQL — mutations that trigger fetches
  • Overview — server architecture