allsource_core/application/use_cases/
manage_schema.rs

1use crate::application::dto::{
2    ListSchemasResponse, RegisterSchemaRequest, RegisterSchemaResponse, SchemaDto,
3    UpdateSchemaRequest,
4};
5use crate::domain::entities::{CompatibilityMode, Schema};
6use crate::error::Result;
7
8/// Use Case: Register Schema
9///
10/// Registers a new schema or creates a new version of an existing schema.
11pub struct RegisterSchemaUseCase;
12
13impl RegisterSchemaUseCase {
14    pub fn execute(request: RegisterSchemaRequest) -> Result<RegisterSchemaResponse> {
15        // Determine compatibility mode (default to None)
16        let compatibility_mode = request
17            .compatibility_mode
18            .map(CompatibilityMode::from)
19            .unwrap_or(CompatibilityMode::None);
20
21        // Create schema (version 1)
22        let mut schema = Schema::new_v1(request.subject, request.schema, compatibility_mode)?;
23
24        // Set description if provided
25        if let Some(description) = request.description {
26            schema.set_description(description)?;
27        }
28
29        // Add tags if provided
30        if let Some(tags) = request.tags {
31            for tag in tags {
32                schema.add_tag(tag)?;
33            }
34        }
35
36        Ok(RegisterSchemaResponse {
37            schema: SchemaDto::from(&schema),
38        })
39    }
40}
41
42/// Use Case: Create Next Schema Version
43///
44/// Creates a new version of an existing schema.
45pub struct CreateNextSchemaVersionUseCase;
46
47impl CreateNextSchemaVersionUseCase {
48    pub fn execute(
49        current_schema: &Schema,
50        new_schema_definition: serde_json::Value,
51        description: Option<String>,
52    ) -> Result<SchemaDto> {
53        // Create next version
54        let mut next_schema = current_schema.create_next_version(new_schema_definition)?;
55
56        // Set description if provided
57        if let Some(desc) = description {
58            next_schema.set_description(desc)?;
59        }
60
61        // Copy tags from previous version
62        for tag in current_schema.tags() {
63            next_schema.add_tag(tag.clone())?;
64        }
65
66        Ok(SchemaDto::from(&next_schema))
67    }
68}
69
70/// Use Case: Update Schema Metadata
71///
72/// Updates schema description and tags (doesn't change the schema itself).
73pub struct UpdateSchemaMetadataUseCase;
74
75impl UpdateSchemaMetadataUseCase {
76    pub fn execute(mut schema: Schema, request: UpdateSchemaRequest) -> Result<SchemaDto> {
77        // Update description if provided
78        if let Some(description) = request.description {
79            if description.is_empty() {
80                schema.clear_description();
81            } else {
82                schema.set_description(description)?;
83            }
84        }
85
86        // Update tags if provided
87        if let Some(tags) = request.tags {
88            // Clear existing tags and add new ones
89            for existing_tag in schema.tags().to_vec() {
90                schema.remove_tag(&existing_tag);
91            }
92            for tag in tags {
93                schema.add_tag(tag)?;
94            }
95        }
96
97        Ok(SchemaDto::from(&schema))
98    }
99}
100
101/// Use Case: List Schemas
102///
103/// Returns a list of all schemas for a subject.
104pub struct ListSchemasUseCase;
105
106impl ListSchemasUseCase {
107    pub fn execute(schemas: Vec<Schema>) -> ListSchemasResponse {
108        let schema_dtos: Vec<SchemaDto> = schemas.iter().map(SchemaDto::from).collect();
109        let count = schema_dtos.len();
110
111        ListSchemasResponse {
112            schemas: schema_dtos,
113            count,
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use crate::application::dto::CompatibilityModeDto;
122    use serde_json::json;
123
124    #[test]
125    fn test_register_schema() {
126        let request = RegisterSchemaRequest {
127            subject: "user.created".to_string(),
128            schema: json!({
129                "type": "object",
130                "properties": {
131                    "name": {"type": "string"},
132                    "email": {"type": "string"}
133                }
134            }),
135            compatibility_mode: Some(CompatibilityModeDto::Backward),
136            description: Some("User creation event schema".to_string()),
137            tags: Some(vec!["user".to_string(), "core".to_string()]),
138        };
139
140        let response = RegisterSchemaUseCase::execute(request);
141        assert!(response.is_ok());
142
143        let response = response.unwrap();
144        assert_eq!(response.schema.subject, "user.created");
145        assert_eq!(response.schema.version, 1);
146        assert_eq!(response.schema.tags.len(), 2);
147        assert_eq!(
148            response.schema.compatibility_mode,
149            CompatibilityModeDto::Backward
150        );
151    }
152
153    #[test]
154    fn test_create_next_version() {
155        let schema = Schema::new_v1(
156            "order.placed".to_string(),
157            json!({"type": "object"}),
158            CompatibilityMode::None,
159        )
160        .unwrap();
161
162        let new_schema = json!({
163            "type": "object",
164            "properties": {
165                "amount": {"type": "number"}
166            }
167        });
168
169        let result = CreateNextSchemaVersionUseCase::execute(
170            &schema,
171            new_schema,
172            Some("Version 2".to_string()),
173        );
174
175        assert!(result.is_ok());
176        let next = result.unwrap();
177        assert_eq!(next.version, 2);
178        assert_eq!(next.subject, "order.placed");
179    }
180
181    #[test]
182    fn test_update_schema_metadata() {
183        let mut schema = Schema::new_v1(
184            "test.event".to_string(),
185            json!({"type": "object"}),
186            CompatibilityMode::None,
187        )
188        .unwrap();
189
190        schema.add_tag("old-tag".to_string()).unwrap();
191
192        let request = UpdateSchemaRequest {
193            description: Some("Updated description".to_string()),
194            tags: Some(vec!["new-tag".to_string()]),
195            compatibility_mode: None,
196        };
197
198        let result = UpdateSchemaMetadataUseCase::execute(schema, request);
199        assert!(result.is_ok());
200
201        let updated = result.unwrap();
202        assert_eq!(updated.description, Some("Updated description".to_string()));
203        assert_eq!(updated.tags, vec!["new-tag".to_string()]);
204    }
205
206    #[test]
207    fn test_list_schemas() {
208        let schemas = vec![
209            Schema::new_v1(
210                "event.one".to_string(),
211                json!({"type": "object"}),
212                CompatibilityMode::None,
213            )
214            .unwrap(),
215            Schema::new_v1(
216                "event.two".to_string(),
217                json!({"type": "object"}),
218                CompatibilityMode::Backward,
219            )
220            .unwrap(),
221        ];
222
223        let response = ListSchemasUseCase::execute(schemas);
224        assert_eq!(response.count, 2);
225        assert_eq!(response.schemas.len(), 2);
226    }
227}