koprogo_api/infrastructure/
openapi.rs

1/// OpenAPI Documentation Module
2/// Generates OpenAPI 3.0 specification for KoproGo API
3/// Access Swagger UI at: http://localhost:8080/swagger-ui/
4use utoipa::{
5    openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
6    Modify, OpenApi,
7};
8use utoipa_swagger_ui::SwaggerUi;
9
10// Import annotated handlers
11// TODO: Re-enable when health_check handler is implemented
12// use crate::infrastructure::web::handlers::health::health_check;
13
14/// Main OpenAPI documentation structure
15#[derive(OpenApi)]
16#[openapi(
17    info(
18        title = "KoproGo API",
19        version = "1.0.0",
20        description = "Belgian Property Management SaaS Platform\n\n\
21            # Features\n\
22            - ๐Ÿข Building & Unit Management\n\
23            - ๐Ÿ‘ฅ Multi-owner & Multi-role Support\n\
24            - ๐Ÿ’ฐ Financial Management (Belgian PCMN)\n\
25            - ๐Ÿ—ณ๏ธ Meeting & Voting System\n\
26            - ๐Ÿ“„ Document Management\n\
27            - ๐Ÿ“Š Budget & ร‰tat Datรฉ Generation\n\
28            - ๐Ÿ”” Notifications & Payment Recovery\n\
29            - ๐Ÿค Community Features (SEL, Notices, Skills)\n\
30            - ๐ŸŽฎ Gamification & Achievements\n\
31            - ๐Ÿ” GDPR Compliant\n\n\
32            # Authentication\n\
33            All endpoints (except /health and /public/*) require JWT Bearer token.\n\
34            Get token via POST /api/v1/auth/login\n\n\
35            # Complete API Documentation\n\
36            This OpenAPI spec is incrementally being built. Currently includes:\n\
37            - Health check endpoint\n\
38            - ~400 additional endpoints available (see routes.rs)\n\n\
39            To add endpoints to this spec, annotate handlers with #[utoipa::path(...)]\n\
40            See health.rs for example implementation.",
41        contact(
42            name = "KoproGo Support",
43            email = "support@koprogo.com"
44        ),
45        license(
46            name = "AGPL-3.0-or-later",
47            url = "https://www.gnu.org/licenses/agpl-3.0.en.html"
48        ),
49    ),
50    servers(
51        (url = "http://localhost:8080", description = "Local development"),
52        (url = "https://api.koprogo.com", description = "Production"),
53    ),
54    paths(
55        // Health & Monitoring
56        // TODO: Re-enable when health_check handler is implemented
57        // health_check,
58    ),
59    components(
60        schemas(
61            // Add DTOs and entities here as they get annotated
62        )
63    ),
64    modifiers(&SecurityAddon),
65    tags(
66        (name = "Health", description = "System health and monitoring"),
67        (name = "Auth", description = "Authentication and authorization"),
68        (name = "Buildings", description = "Building management"),
69        (name = "Units", description = "Unit management"),
70        (name = "Owners", description = "Owner management"),
71        (name = "Expenses", description = "Expense and invoice management"),
72        (name = "Meetings", description = "General assembly management"),
73        (name = "Budgets", description = "Annual budget management"),
74        (name = "Documents", description = "Document upload/download"),
75        (name = "GDPR", description = "Data privacy compliance"),
76        (name = "Payments", description = "Payment processing"),
77        (name = "PaymentMethods", description = "Stored payment methods"),
78        (name = "LocalExchanges", description = "SEL time-based exchange system"),
79        (name = "Notifications", description = "Multi-channel notifications"),
80        (name = "Tickets", description = "Maintenance request system"),
81        (name = "Resolutions", description = "Meeting voting system"),
82        (name = "BoardMembers", description = "Board of directors management"),
83        (name = "Quotes", description = "Contractor quote management"),
84        (name = "EtatsDates", description = "Property sale documentation"),
85        (name = "PaymentRecovery", description = "Automated payment reminders"),
86    )
87)]
88pub struct ApiDoc;
89
90/// Add JWT Bearer authentication to OpenAPI spec
91struct SecurityAddon;
92
93impl Modify for SecurityAddon {
94    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
95        if let Some(components) = openapi.components.as_mut() {
96            components.add_security_scheme(
97                "bearer_auth",
98                SecurityScheme::Http(
99                    HttpBuilder::new()
100                        .scheme(HttpAuthScheme::Bearer)
101                        .bearer_format("JWT")
102                        .description(Some(
103                            "JWT token obtained from /api/v1/auth/login.\n\n\
104                            Example: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`\n\n\
105                            To authenticate:\n\
106                            1. Click 'Authorize' button above\n\
107                            2. Enter token (with or without 'Bearer ' prefix)\n\
108                            3. Click 'Authorize' in dialog\n\
109                            4. Try endpoints",
110                        ))
111                        .build(),
112                ),
113            )
114        }
115    }
116}
117
118/// Configure Swagger UI service
119///
120/// Swagger UI will be available at: http://localhost:8080/swagger-ui/
121pub fn configure_swagger_ui() -> SwaggerUi {
122    SwaggerUi::new("/swagger-ui/{_:.*}")
123        .url("/api-docs/openapi.json", ApiDoc::openapi())
124        .config(
125            utoipa_swagger_ui::Config::default()
126                .try_it_out_enabled(true)
127                .persist_authorization(true)
128                .display_request_duration(true)
129                .deep_linking(true)
130                .display_operation_id(true)
131                .default_models_expand_depth(1)
132                .default_model_expand_depth(1), // .doc_expansion(utoipa_swagger_ui::DocExpansion::List) // Removed: DocExpansion no longer exists in utoipa_swagger_ui
133        )
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_openapi_spec_generation() {
142        let spec = ApiDoc::openapi();
143
144        // Verify basic info
145        assert_eq!(spec.info.title, "KoproGo API");
146        assert_eq!(spec.info.version, "1.0.0");
147
148        // Verify servers
149        assert!(spec.servers.is_some());
150        let servers = spec.servers.unwrap();
151        assert_eq!(servers.len(), 2);
152
153        // Verify security scheme
154        assert!(spec.components.is_some());
155        let components = spec.components.unwrap();
156        assert!(components.security_schemes.contains_key("bearer_auth"));
157
158        // Verify tags
159        assert!(spec.tags.is_some());
160        let tags = spec.tags.unwrap();
161        assert!(tags.len() >= 15);
162    }
163
164    #[test]
165    fn test_swagger_ui_configuration() {
166        let _swagger = configure_swagger_ui();
167        // SwaggerUi is configured, this test ensures it compiles
168    }
169
170    #[test]
171    fn test_openapi_json_is_valid() {
172        let spec = ApiDoc::openapi();
173
174        // Serialize to JSON to ensure it's valid
175        let json = serde_json::to_string(&spec).expect("Should serialize to JSON");
176        assert!(json.contains("\"title\":\"KoproGo API\""));
177        assert!(json.contains("\"version\":\"1.0.0\""));
178    }
179}