mockforge_vbr/
api_generator.rs1use crate::entities::Entity;
13
14#[derive(Debug, Clone)]
16pub struct ApiEndpoint {
17 pub method: String,
19 pub path: String,
21 pub handler_name: String,
23 pub entity_name: String,
25}
26
27pub struct ApiGenerator {
29 pub prefix: String,
31}
32
33impl ApiGenerator {
34 pub fn new(prefix: String) -> Self {
36 Self { prefix }
37 }
38
39 pub fn generate_endpoints(&self, entity: &Entity) -> Vec<ApiEndpoint> {
41 let entity_name = entity.name().to_lowercase();
42 let mut endpoints = Vec::new();
43
44 endpoints.push(ApiEndpoint {
46 method: "GET".to_string(),
47 path: format!("{}/{}", self.prefix, entity_name),
48 handler_name: format!("list_{}", entity_name),
49 entity_name: entity.name().to_string(),
50 });
51
52 endpoints.push(ApiEndpoint {
54 method: "GET".to_string(),
55 path: format!("{}/{}/{{id}}", self.prefix, entity_name),
56 handler_name: format!("get_{}", entity_name),
57 entity_name: entity.name().to_string(),
58 });
59
60 endpoints.push(ApiEndpoint {
62 method: "POST".to_string(),
63 path: format!("{}/{}", self.prefix, entity_name),
64 handler_name: format!("create_{}", entity_name),
65 entity_name: entity.name().to_string(),
66 });
67
68 endpoints.push(ApiEndpoint {
70 method: "PUT".to_string(),
71 path: format!("{}/{}/{{id}}", self.prefix, entity_name),
72 handler_name: format!("update_{}", entity_name),
73 entity_name: entity.name().to_string(),
74 });
75
76 endpoints.push(ApiEndpoint {
78 method: "PATCH".to_string(),
79 path: format!("{}/{}/{{id}}", self.prefix, entity_name),
80 handler_name: format!("patch_{}", entity_name),
81 entity_name: entity.name().to_string(),
82 });
83
84 endpoints.push(ApiEndpoint {
86 method: "DELETE".to_string(),
87 path: format!("{}/{}/{{id}}", self.prefix, entity_name),
88 handler_name: format!("delete_{}", entity_name),
89 entity_name: entity.name().to_string(),
90 });
91
92 for fk in &entity.schema.foreign_keys {
94 let relationship_name = fk.field.trim_end_matches("_id");
95 endpoints.push(ApiEndpoint {
96 method: "GET".to_string(),
97 path: format!("{}/{}/{{id}}/{}", self.prefix, entity_name, relationship_name),
98 handler_name: format!(
99 "get_{}_by_{}",
100 fk.target_entity.to_lowercase(),
101 relationship_name
102 ),
103 entity_name: entity.name().to_string(),
104 });
105 }
106
107 endpoints
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::entities::Entity;
115 use crate::schema::VbrSchemaDefinition;
116 use mockforge_data::SchemaDefinition;
117
118 fn create_test_entity(name: &str) -> Entity {
119 let base_schema = SchemaDefinition::new(name.to_string());
120 let vbr_schema = VbrSchemaDefinition::new(base_schema);
121 Entity::new(name.to_string(), vbr_schema)
122 }
123
124 fn create_entity_with_foreign_key(name: &str) -> Entity {
125 let base_schema = SchemaDefinition::new(name.to_string());
126 let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
127 vbr_schema.foreign_keys.push(crate::schema::ForeignKeyDefinition {
128 field: "user_id".to_string(),
129 target_entity: "User".to_string(),
130 target_field: "id".to_string(),
131 on_delete: crate::schema::CascadeAction::Cascade,
132 on_update: crate::schema::CascadeAction::NoAction,
133 });
134 Entity::new(name.to_string(), vbr_schema)
135 }
136
137 #[test]
138 fn test_api_generator_new() {
139 let generator = ApiGenerator::new("/api".to_string());
140 assert_eq!(generator.prefix, "/api");
141 }
142
143 #[test]
144 fn test_api_generator_custom_prefix() {
145 let generator = ApiGenerator::new("/v2/api".to_string());
146 assert_eq!(generator.prefix, "/v2/api");
147 }
148
149 #[test]
150 fn test_generate_endpoints_count() {
151 let generator = ApiGenerator::new("/api".to_string());
152 let entity = create_test_entity("User");
153 let endpoints = generator.generate_endpoints(&entity);
154
155 assert_eq!(endpoints.len(), 6);
157 }
158
159 #[test]
160 fn test_generate_endpoints_methods() {
161 let generator = ApiGenerator::new("/api".to_string());
162 let entity = create_test_entity("User");
163 let endpoints = generator.generate_endpoints(&entity);
164
165 let methods: Vec<&str> = endpoints.iter().map(|e| e.method.as_str()).collect();
166 assert!(methods.contains(&"GET"));
167 assert!(methods.contains(&"POST"));
168 assert!(methods.contains(&"PUT"));
169 assert!(methods.contains(&"PATCH"));
170 assert!(methods.contains(&"DELETE"));
171 }
172
173 #[test]
174 fn test_generate_endpoints_paths() {
175 let generator = ApiGenerator::new("/api".to_string());
176 let entity = create_test_entity("User");
177 let endpoints = generator.generate_endpoints(&entity);
178
179 let paths: Vec<&str> = endpoints.iter().map(|e| e.path.as_str()).collect();
180 assert!(paths.contains(&"/api/user"));
181 assert!(paths.contains(&"/api/user/{id}"));
182 }
183
184 #[test]
185 fn test_generate_endpoints_handler_names() {
186 let generator = ApiGenerator::new("/api".to_string());
187 let entity = create_test_entity("User");
188 let endpoints = generator.generate_endpoints(&entity);
189
190 let handlers: Vec<&str> = endpoints.iter().map(|e| e.handler_name.as_str()).collect();
191 assert!(handlers.contains(&"list_user"));
192 assert!(handlers.contains(&"get_user"));
193 assert!(handlers.contains(&"create_user"));
194 assert!(handlers.contains(&"update_user"));
195 assert!(handlers.contains(&"patch_user"));
196 assert!(handlers.contains(&"delete_user"));
197 }
198
199 #[test]
200 fn test_generate_endpoints_entity_name() {
201 let generator = ApiGenerator::new("/api".to_string());
202 let entity = create_test_entity("Product");
203 let endpoints = generator.generate_endpoints(&entity);
204
205 for endpoint in endpoints {
206 assert_eq!(endpoint.entity_name, "Product");
207 }
208 }
209
210 #[test]
211 fn test_generate_endpoints_with_foreign_key() {
212 let generator = ApiGenerator::new("/api".to_string());
213 let entity = create_entity_with_foreign_key("Order");
214 let endpoints = generator.generate_endpoints(&entity);
215
216 assert_eq!(endpoints.len(), 7);
218
219 let relationship_endpoint = endpoints.iter().find(|e| e.path.contains("/user"));
221 assert!(relationship_endpoint.is_some());
222 assert_eq!(relationship_endpoint.unwrap().method, "GET");
223 }
224
225 #[test]
226 fn test_api_endpoint_debug() {
227 let endpoint = ApiEndpoint {
228 method: "GET".to_string(),
229 path: "/api/test".to_string(),
230 handler_name: "test_handler".to_string(),
231 entity_name: "Test".to_string(),
232 };
233
234 let debug = format!("{:?}", endpoint);
235 assert!(debug.contains("ApiEndpoint"));
236 assert!(debug.contains("GET"));
237 assert!(debug.contains("/api/test"));
238 }
239
240 #[test]
241 fn test_api_endpoint_clone() {
242 let endpoint = ApiEndpoint {
243 method: "POST".to_string(),
244 path: "/api/test".to_string(),
245 handler_name: "create_test".to_string(),
246 entity_name: "Test".to_string(),
247 };
248
249 let cloned = endpoint.clone();
250 assert_eq!(endpoint.method, cloned.method);
251 assert_eq!(endpoint.path, cloned.path);
252 assert_eq!(endpoint.handler_name, cloned.handler_name);
253 assert_eq!(endpoint.entity_name, cloned.entity_name);
254 }
255
256 #[test]
257 fn test_lowercase_entity_in_path() {
258 let generator = ApiGenerator::new("/api".to_string());
259 let entity = create_test_entity("UserProfile");
260 let endpoints = generator.generate_endpoints(&entity);
261
262 let list_endpoint = endpoints.iter().find(|e| e.handler_name == "list_userprofile");
264 assert!(list_endpoint.is_some());
265 assert_eq!(list_endpoint.unwrap().path, "/api/userprofile");
266 }
267}