Skip to main content

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// Handler imports not needed โ€” utoipa paths() uses full module paths
11
12/// Main OpenAPI documentation structure
13#[derive(OpenApi)]
14#[openapi(
15    info(
16        title = "KoproGo API",
17        version = "1.0.0",
18        description = "Belgian Property Management SaaS Platform\n\n\
19            # Features\n\
20            - ๐Ÿข Building & Unit Management\n\
21            - ๐Ÿ‘ฅ Multi-owner & Multi-role Support\n\
22            - ๐Ÿ’ฐ Financial Management (Belgian PCMN)\n\
23            - ๐Ÿ—ณ๏ธ Meeting & Voting System\n\
24            - ๐Ÿ“„ Document Management\n\
25            - ๐Ÿ“Š Budget & ร‰tat Datรฉ Generation\n\
26            - ๐Ÿ”” Notifications & Payment Recovery\n\
27            - ๐Ÿค Community Features (SEL, Notices, Skills)\n\
28            - ๐ŸŽฎ Gamification & Achievements\n\
29            - ๐Ÿ” GDPR Compliant\n\n\
30            # Authentication\n\
31            All endpoints (except /health and /public/*) require JWT Bearer token.\n\
32            Get token via POST /api/v1/auth/login\n\n\
33            # Complete API Documentation\n\
34            90 of 511 endpoints annotated with utoipa (Swagger UI live spec).\n\
35            Full 495-endpoint OpenAPI 3.0.3 spec available at docs/api/openapi.yaml.\n\n\
36            Progressive annotation ongoing โ€” see handlers for pattern.",
37        contact(
38            name = "KoproGo Support",
39            email = "support@koprogo.com"
40        ),
41        license(
42            name = "AGPL-3.0-or-later",
43            url = "https://www.gnu.org/licenses/agpl-3.0.en.html"
44        ),
45    ),
46    servers(
47        (url = "http://localhost:8080", description = "Local development"),
48        (url = "https://api.koprogo.com", description = "Production"),
49    ),
50    paths(
51        // Health
52        crate::infrastructure::web::handlers::health::health_check,
53        // Auth
54        crate::infrastructure::web::handlers::auth_handlers::login,
55        crate::infrastructure::web::handlers::auth_handlers::register,
56        crate::infrastructure::web::handlers::auth_handlers::refresh_token,
57        crate::infrastructure::web::handlers::auth_handlers::switch_role,
58        crate::infrastructure::web::handlers::auth_handlers::get_current_user,
59        // Buildings
60        crate::infrastructure::web::handlers::building_handlers::create_building,
61        crate::infrastructure::web::handlers::building_handlers::list_buildings,
62        crate::infrastructure::web::handlers::building_handlers::get_building,
63        crate::infrastructure::web::handlers::building_handlers::update_building,
64        crate::infrastructure::web::handlers::building_handlers::delete_building,
65        crate::infrastructure::web::handlers::building_handlers::export_annual_report_pdf,
66        // Payments
67        crate::infrastructure::web::handlers::payment_handlers::create_payment,
68        crate::infrastructure::web::handlers::payment_handlers::get_payment,
69        crate::infrastructure::web::handlers::payment_handlers::get_payment_by_stripe_intent,
70        crate::infrastructure::web::handlers::payment_handlers::list_owner_payments,
71        crate::infrastructure::web::handlers::payment_handlers::list_building_payments,
72        crate::infrastructure::web::handlers::payment_handlers::list_expense_payments,
73        crate::infrastructure::web::handlers::payment_handlers::list_organization_payments,
74        crate::infrastructure::web::handlers::payment_handlers::list_payments_by_status,
75        crate::infrastructure::web::handlers::payment_handlers::list_pending_payments,
76        crate::infrastructure::web::handlers::payment_handlers::list_failed_payments,
77        crate::infrastructure::web::handlers::payment_handlers::mark_payment_processing,
78        crate::infrastructure::web::handlers::payment_handlers::mark_payment_requires_action,
79        crate::infrastructure::web::handlers::payment_handlers::mark_payment_succeeded,
80        crate::infrastructure::web::handlers::payment_handlers::mark_payment_failed,
81        crate::infrastructure::web::handlers::payment_handlers::mark_payment_cancelled,
82        crate::infrastructure::web::handlers::payment_handlers::refund_payment,
83        crate::infrastructure::web::handlers::payment_handlers::delete_payment,
84        crate::infrastructure::web::handlers::payment_handlers::get_owner_payment_stats,
85        crate::infrastructure::web::handlers::payment_handlers::get_building_payment_stats,
86        crate::infrastructure::web::handlers::payment_handlers::get_expense_total_paid,
87        crate::infrastructure::web::handlers::payment_handlers::get_owner_total_paid,
88        crate::infrastructure::web::handlers::payment_handlers::get_building_total_paid,
89        // Tickets
90        crate::infrastructure::web::handlers::ticket_handlers::create_ticket,
91        crate::infrastructure::web::handlers::ticket_handlers::get_ticket,
92        crate::infrastructure::web::handlers::ticket_handlers::delete_ticket,
93        crate::infrastructure::web::handlers::ticket_handlers::list_my_tickets,
94        crate::infrastructure::web::handlers::ticket_handlers::list_assigned_tickets,
95        crate::infrastructure::web::handlers::ticket_handlers::list_building_tickets,
96        crate::infrastructure::web::handlers::ticket_handlers::list_organization_tickets,
97        crate::infrastructure::web::handlers::ticket_handlers::list_tickets_by_status,
98        crate::infrastructure::web::handlers::ticket_handlers::assign_ticket,
99        crate::infrastructure::web::handlers::ticket_handlers::start_work,
100        crate::infrastructure::web::handlers::ticket_handlers::resolve_ticket,
101        crate::infrastructure::web::handlers::ticket_handlers::close_ticket,
102        crate::infrastructure::web::handlers::ticket_handlers::cancel_ticket,
103        crate::infrastructure::web::handlers::ticket_handlers::reopen_ticket,
104        crate::infrastructure::web::handlers::ticket_handlers::get_ticket_statistics,
105        crate::infrastructure::web::handlers::ticket_handlers::get_ticket_statistics_org,
106        crate::infrastructure::web::handlers::ticket_handlers::get_overdue_tickets,
107        crate::infrastructure::web::handlers::ticket_handlers::get_overdue_tickets_org,
108        // Polls
109        crate::infrastructure::web::handlers::poll_handlers::create_poll,
110        crate::infrastructure::web::handlers::poll_handlers::get_poll,
111        crate::infrastructure::web::handlers::poll_handlers::update_poll,
112        crate::infrastructure::web::handlers::poll_handlers::list_polls,
113        crate::infrastructure::web::handlers::poll_handlers::find_active_polls,
114        crate::infrastructure::web::handlers::poll_handlers::publish_poll,
115        crate::infrastructure::web::handlers::poll_handlers::close_poll,
116        crate::infrastructure::web::handlers::poll_handlers::cancel_poll,
117        crate::infrastructure::web::handlers::poll_handlers::delete_poll,
118        crate::infrastructure::web::handlers::poll_handlers::cast_poll_vote,
119        crate::infrastructure::web::handlers::poll_handlers::get_poll_results,
120        crate::infrastructure::web::handlers::poll_handlers::get_poll_building_statistics,
121        // Resolutions
122        crate::infrastructure::web::handlers::resolution_handlers::create_resolution,
123        crate::infrastructure::web::handlers::resolution_handlers::get_resolution,
124        crate::infrastructure::web::handlers::resolution_handlers::list_meeting_resolutions,
125        crate::infrastructure::web::handlers::resolution_handlers::delete_resolution,
126        crate::infrastructure::web::handlers::resolution_handlers::cast_vote,
127        crate::infrastructure::web::handlers::resolution_handlers::list_resolution_votes,
128        crate::infrastructure::web::handlers::resolution_handlers::change_vote,
129        crate::infrastructure::web::handlers::resolution_handlers::close_voting,
130        crate::infrastructure::web::handlers::resolution_handlers::get_meeting_vote_summary,
131        // Notifications
132        crate::infrastructure::web::handlers::notification_handlers::create_notification,
133        crate::infrastructure::web::handlers::notification_handlers::get_notification,
134        crate::infrastructure::web::handlers::notification_handlers::list_my_notifications,
135        crate::infrastructure::web::handlers::notification_handlers::list_unread_notifications,
136        crate::infrastructure::web::handlers::notification_handlers::mark_notification_read,
137        crate::infrastructure::web::handlers::notification_handlers::mark_all_notifications_read,
138        crate::infrastructure::web::handlers::notification_handlers::delete_notification,
139        crate::infrastructure::web::handlers::notification_handlers::get_notification_stats,
140        crate::infrastructure::web::handlers::notification_handlers::get_user_preferences,
141        crate::infrastructure::web::handlers::notification_handlers::get_preference,
142        crate::infrastructure::web::handlers::notification_handlers::update_preference,
143        // GDPR
144        crate::infrastructure::web::handlers::gdpr_handlers::export_user_data,
145        crate::infrastructure::web::handlers::gdpr_handlers::erase_user_data,
146        crate::infrastructure::web::handlers::gdpr_handlers::can_erase_user,
147        crate::infrastructure::web::handlers::gdpr_handlers::rectify_user_data,
148        crate::infrastructure::web::handlers::gdpr_handlers::restrict_user_processing,
149        crate::infrastructure::web::handlers::gdpr_handlers::set_marketing_preference,
150        // Consent (GDPR)
151        crate::infrastructure::web::handlers::consent_handlers::record_consent,
152        crate::infrastructure::web::handlers::consent_handlers::get_consent_status,
153        // Legal Reference
154        crate::infrastructure::web::handlers::legal_handlers::list_legal_rules,
155        crate::infrastructure::web::handlers::legal_handlers::get_legal_rule,
156        crate::infrastructure::web::handlers::legal_handlers::get_ag_sequence,
157        crate::infrastructure::web::handlers::legal_handlers::get_majority_for,
158    ),
159    components(schemas(
160        // Pagination primitives โ€” referenced by query params on list endpoints
161        crate::application::dto::pagination::SortOrder,
162        // STORY-P7-701/702: all enums used by frontend wrappers are exposed
163        // here so `openapi-typescript` emits them into api.d.ts for re-export.
164        crate::domain::entities::resolution::ResolutionType,
165        crate::domain::entities::resolution::MajorityType,
166        crate::domain::entities::ticket::TicketCategory,
167        crate::domain::entities::ticket::TicketPriority,
168        crate::domain::entities::ticket::TicketStatus,
169        crate::domain::entities::poll::PollStatus,
170        crate::domain::entities::poll::PollType,
171        crate::domain::entities::meeting::MeetingType,
172        crate::domain::entities::meeting::MeetingStatus,
173        crate::domain::entities::expense::ExpenseCategory,
174        crate::domain::entities::expense::PaymentStatus,
175        crate::domain::entities::expense::ApprovalStatus,
176        crate::domain::entities::resource_booking::ResourceType,
177        crate::domain::entities::resource_booking::BookingStatus,
178        crate::domain::entities::resource_booking::RecurringPattern,
179        crate::domain::entities::shared_object::SharedObjectCategory,
180        crate::domain::entities::shared_object::ObjectCondition,
181        crate::domain::entities::budget::BudgetStatus,
182        crate::domain::entities::convocation::ConvocationType,
183        crate::domain::entities::convocation::ConvocationStatus,
184        crate::domain::entities::convocation_recipient::AttendanceStatus,
185        crate::domain::entities::energy_campaign::CampaignType,
186        crate::domain::entities::energy_campaign::CampaignStatus,
187        crate::domain::entities::energy_campaign::EnergyType,
188        crate::domain::entities::energy_campaign::ContractType,
189        crate::domain::entities::etat_date::EtatDateStatus,
190        crate::domain::entities::etat_date::EtatDateLanguage,
191        crate::domain::entities::achievement::AchievementCategory,
192        crate::domain::entities::achievement::AchievementTier,
193        crate::domain::entities::challenge::ChallengeStatus,
194        crate::domain::entities::challenge::ChallengeType,
195        crate::domain::entities::technical_inspection::InspectionType,
196        crate::domain::entities::technical_inspection::InspectionStatus,
197        crate::domain::entities::local_exchange::ExchangeType,
198        crate::domain::entities::local_exchange::ExchangeStatus,
199        crate::domain::entities::owner_credit_balance::CreditStatus,
200        crate::domain::entities::owner_credit_balance::ParticipationLevel,
201        crate::domain::entities::notice::NoticeType,
202        crate::domain::entities::notice::NoticeCategory,
203        crate::domain::entities::notice::NoticeStatus,
204        crate::domain::entities::payment_reminder::ReminderLevel,
205        crate::domain::entities::payment_reminder::ReminderStatus,
206        crate::domain::entities::payment_reminder::DeliveryMethod,
207        crate::domain::entities::quote::QuoteStatus,
208        crate::domain::entities::skill::SkillCategory,
209        crate::domain::entities::skill::ExpertiseLevel,
210        crate::domain::entities::work_report::WorkType,
211        crate::domain::entities::work_report::WarrantyType,
212        crate::domain::entities::resolution::ResolutionStatus,
213        crate::domain::entities::notification::NotificationStatus,
214        crate::domain::entities::notification::NotificationPriority,
215        crate::domain::entities::payment::TransactionStatus,
216        crate::domain::entities::payment_method::PaymentMethodType,
217    )),
218    modifiers(&SecurityAddon),
219    tags(
220        (name = "Health", description = "System health and monitoring"),
221        (name = "Auth", description = "Authentication and authorization"),
222        (name = "Buildings", description = "Building management"),
223        (name = "Units", description = "Unit management"),
224        (name = "Owners", description = "Owner management"),
225        (name = "Expenses", description = "Expense and invoice management"),
226        (name = "Meetings", description = "General assembly management"),
227        (name = "Budgets", description = "Annual budget management"),
228        (name = "Documents", description = "Document upload/download"),
229        (name = "GDPR", description = "Data privacy compliance"),
230        (name = "Payments", description = "Payment processing"),
231        (name = "PaymentMethods", description = "Stored payment methods"),
232        (name = "LocalExchanges", description = "SEL time-based exchange system"),
233        (name = "Notifications", description = "Multi-channel notifications"),
234        (name = "Tickets", description = "Maintenance request system"),
235        (name = "Resolutions", description = "Meeting voting system"),
236        (name = "BoardMembers", description = "Board of directors management"),
237        (name = "Quotes", description = "Contractor quote management"),
238        (name = "EtatsDates", description = "Property sale documentation"),
239        (name = "PaymentRecovery", description = "Automated payment reminders"),
240        (name = "Consent", description = "User consent management (GDPR Art. 7)"),
241        (name = "Legal Reference", description = "Belgian legal reference rules and majority types"),
242    )
243)]
244pub struct ApiDoc;
245
246/// Add JWT Bearer authentication to OpenAPI spec
247struct SecurityAddon;
248
249impl Modify for SecurityAddon {
250    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
251        if let Some(components) = openapi.components.as_mut() {
252            components.add_security_scheme(
253                "bearer_auth",
254                SecurityScheme::Http(
255                    HttpBuilder::new()
256                        .scheme(HttpAuthScheme::Bearer)
257                        .bearer_format("JWT")
258                        .description(Some(
259                            "JWT token obtained from /api/v1/auth/login.\n\n\
260                            Example: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`\n\n\
261                            To authenticate:\n\
262                            1. Click 'Authorize' button above\n\
263                            2. Enter token (with or without 'Bearer ' prefix)\n\
264                            3. Click 'Authorize' in dialog\n\
265                            4. Try endpoints",
266                        ))
267                        .build(),
268                ),
269            )
270        }
271    }
272}
273
274/// Configure Swagger UI service
275///
276/// Swagger UI will be available at: http://localhost:8080/swagger-ui/
277pub fn configure_swagger_ui() -> SwaggerUi {
278    SwaggerUi::new("/swagger-ui/{_:.*}")
279        .url("/api-docs/openapi.json", ApiDoc::openapi())
280        .config(
281            utoipa_swagger_ui::Config::default()
282                .try_it_out_enabled(true)
283                .persist_authorization(true)
284                .display_request_duration(true)
285                .deep_linking(true)
286                .display_operation_id(true)
287                .default_models_expand_depth(1)
288                .default_model_expand_depth(1), // .doc_expansion(utoipa_swagger_ui::DocExpansion::List) // Removed: DocExpansion no longer exists in utoipa_swagger_ui
289        )
290}
291
292#[cfg(test)]
293mod tests {
294    use super::*;
295
296    #[test]
297    fn test_openapi_spec_generation() {
298        let spec = ApiDoc::openapi();
299
300        // Verify basic info
301        assert_eq!(spec.info.title, "KoproGo API");
302        assert_eq!(spec.info.version, "1.0.0");
303
304        // Verify servers
305        assert!(spec.servers.is_some());
306        let servers = spec.servers.unwrap();
307        assert_eq!(servers.len(), 2);
308
309        // Verify security scheme
310        assert!(spec.components.is_some());
311        let components = spec.components.unwrap();
312        assert!(components.security_schemes.contains_key("bearer_auth"));
313
314        // Verify tags
315        assert!(spec.tags.is_some());
316        let tags = spec.tags.unwrap();
317        assert!(tags.len() >= 15);
318    }
319
320    #[test]
321    fn test_swagger_ui_configuration() {
322        let _swagger = configure_swagger_ui();
323        // SwaggerUi is configured, this test ensures it compiles
324    }
325
326    #[test]
327    fn test_openapi_json_is_valid() {
328        let spec = ApiDoc::openapi();
329
330        // Serialize to JSON to ensure it's valid
331        let json = serde_json::to_string(&spec).expect("Should serialize to JSON");
332        assert!(json.contains("\"title\":\"KoproGo API\""));
333        assert!(json.contains("\"version\":\"1.0.0\""));
334    }
335}