Skip to main content

allsource_core/application/use_cases/
manage_schema.rs

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