Skip to main content

uptrakit_surfaces/
params.rs

1use serde::{Deserialize, Serialize};
2
3use crate::SchemaContract;
4
5/// Framework-reserved / envelope query keys. Provider-declared `params` keys
6/// must not collide with these (admission rule, spec ยง4 rule 1). `id` is the
7/// B5 item-addressing key populated from the `/{item_id}` path segment.
8pub const RESERVED_PARAM_KEYS: &[&str] = &[
9    "page",
10    "per_page",
11    "target_provider_id",
12    "timeout_seconds",
13    "id",
14];
15
16/// Opt-in per-field parameter declaration on an interaction. Declared fields
17/// get strict typed parsing on GET query strings and per-field body
18/// validation on mutating methods; undeclared keys pass through untyped.
19#[non_exhaustive]
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct ParamFieldDescriptor {
22    pub key: String,
23    pub schema: SchemaContract,
24    #[serde(default)]
25    pub required: bool,
26}
27
28impl ParamFieldDescriptor {
29    pub fn new(key: impl Into<String>, schema: SchemaContract) -> Self {
30        Self {
31            key: key.into(),
32            schema,
33            required: false,
34        }
35    }
36
37    #[must_use]
38    pub fn required(mut self) -> Self {
39        self.required = true;
40        self
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn param_field_descriptor_defaults_required_false_on_wire() {
50        let json = serde_json::json!({ "key": "plugin_config_id", "schema": { "type": "string" } });
51        let field: ParamFieldDescriptor = serde_json::from_value(json).expect("deserialize");
52        assert!(!field.required);
53        assert_eq!(field.schema, crate::SchemaContract::String);
54    }
55
56    #[test]
57    fn reserved_keys_cover_the_b5_id_key() {
58        assert!(RESERVED_PARAM_KEYS.contains(&"id"));
59        assert!(RESERVED_PARAM_KEYS.contains(&"page"));
60        assert!(RESERVED_PARAM_KEYS.contains(&"per_page"));
61        assert!(RESERVED_PARAM_KEYS.contains(&"target_provider_id"));
62        assert!(RESERVED_PARAM_KEYS.contains(&"timeout_seconds"));
63    }
64}