Skip to main content

lenso_contracts/
admin_schema.rs

1//! Schema-admin data contracts: a module's declared manageable entities.
2
3use serde::{Deserialize, Serialize};
4
5/// A module's declared admin surface: which entities it exposes for management.
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
7pub struct AdminSchema {
8    pub entities: Vec<EntitySchema>,
9}
10
11/// One manageable entity (e.g. identity's "users").
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
13pub struct EntitySchema {
14    /// Stable entity key, unique within the module, e.g. "users".
15    pub name: String,
16    /// Human label for the console, e.g. "Users".
17    pub label: String,
18    /// Ordered field descriptors driving list columns / detail rows.
19    pub fields: Vec<FieldSchema>,
20    /// Capability required to read this entity. Declared now; gated only
21    /// coarsely (AdminActor) this step. Fine-grained RBAC is a later spec.
22    pub read_capability: String,
23}
24
25/// One field of an entity.
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
27pub struct FieldSchema {
28    /// Key in the record's JSON object, e.g. "email".
29    pub name: String,
30    /// Human label, e.g. "Email".
31    pub label: String,
32    /// Rendering hint for the console's display layer.
33    pub field_type: FieldType,
34    /// Whether the value may be null/absent.
35    #[serde(default)]
36    pub nullable: bool,
37}
38
39/// Minimal field-type vocabulary. `Json` is the catch-all so any field renders.
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
41#[serde(tag = "kind", rename_all = "snake_case")]
42#[non_exhaustive]
43pub enum FieldType {
44    String,
45    Integer,
46    Boolean,
47    Timestamp,
48    Json,
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    fn sample() -> AdminSchema {
56        AdminSchema {
57            entities: vec![EntitySchema {
58                name: "users".to_owned(),
59                label: "Users".to_owned(),
60                read_capability: "identity.users.read".to_owned(),
61                fields: vec![
62                    FieldSchema {
63                        name: "email".into(),
64                        label: "Email".into(),
65                        field_type: FieldType::String,
66                        nullable: false,
67                    },
68                    FieldSchema {
69                        name: "created_at".into(),
70                        label: "Created".into(),
71                        field_type: FieldType::Timestamp,
72                        nullable: false,
73                    },
74                ],
75            }],
76        }
77    }
78
79    #[test]
80    fn admin_schema_round_trips_through_json() {
81        let schema = sample();
82        let json = serde_json::to_string(&schema).expect("serialize");
83        let back: AdminSchema = serde_json::from_str(&json).expect("deserialize");
84        assert_eq!(schema, back);
85    }
86
87    #[test]
88    fn field_type_serializes_with_kind_tag() {
89        let json = serde_json::to_string(&FieldType::Timestamp).expect("serialize");
90        assert_eq!(json, r#"{"kind":"timestamp"}"#);
91    }
92}