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        // ACP (Association des Copropriétaires)
54        //
55        // Les 5 handlers portaient DÉJÀ `#[utoipa::path]` et leurs DTO
56        // `#[derive(ToSchema)]`, mais rien n'était déclaré ici — utoipa ne
57        // collecte QUE ce qui est listé. Les annotations étaient donc mortes :
58        // aucun path `/acps` dans `docs/api/openapi.json`, aucun type dans
59        // `api.d.ts`, et un `CreateAcpDto` recopié à la main côté frontend qui
60        // avait déjà divergé (il omettait `total_tantiemes`). Même défaut que
61        // les 16 endpoints `payment-methods` (#732).
62        crate::infrastructure::web::handlers::acp_handlers::create_acp,
63        crate::infrastructure::web::handlers::acp_handlers::list_acps,
64        crate::infrastructure::web::handlers::acp_handlers::list_acps_with_metrics,
65        crate::infrastructure::web::handlers::acp_handlers::get_acp,
66        crate::infrastructure::web::handlers::acp_handlers::update_acp,
67        crate::infrastructure::web::handlers::acp_handlers::archive_acp,
68        // Registre de modules (Story 5.1 #585, ADR-0015). Une fois au
69        // schéma, le frontend peut remplacer son DTO écrit à la main
70        // (`modules.ts`) par le type généré.
71        crate::infrastructure::web::handlers::acp_module_handlers::list_acp_modules,
72        crate::infrastructure::web::handlers::acp_module_handlers::enable_acp_module,
73        crate::infrastructure::web::handlers::acp_module_handlers::disable_acp_module,
74        // Huit routes arrivées par les branches d'agents sans annotation
75        // (#732 : une route hors spec est invisible au gate anti-drift, qui
76        // compare deux fichiers qui l'ignorent tous les deux).
77        crate::infrastructure::web::handlers::cdc_handlers::create_cdc_alert,
78        crate::infrastructure::web::handlers::cdc_handlers::list_cdc_alerts_for_meeting,
79        crate::infrastructure::web::handlers::cdc_handlers::elect_cdc_members,
80        crate::infrastructure::web::handlers::convocation_handlers::list_eligible_convocation_recipients,
81        crate::infrastructure::web::handlers::etat_date_handlers::issue_notary_link,
82        crate::infrastructure::web::handlers::etat_date_handlers::renew_notary_link,
83        crate::infrastructure::web::handlers::etat_date_handlers::revoke_notary_link,
84        crate::infrastructure::web::handlers::unit_owner_handlers::designate_voting_representative,
85        // Auth
86        crate::infrastructure::web::handlers::auth_handlers::login,
87        crate::infrastructure::web::handlers::auth_handlers::register,
88        crate::infrastructure::web::handlers::auth_handlers::refresh_token,
89        crate::infrastructure::web::handlers::auth_handlers::switch_role,
90        crate::infrastructure::web::handlers::auth_handlers::get_current_user,
91        // Buildings
92        crate::infrastructure::web::handlers::building_handlers::create_building,
93        crate::infrastructure::web::handlers::building_handlers::list_buildings,
94        crate::infrastructure::web::handlers::building_handlers::get_building,
95        crate::infrastructure::web::handlers::building_handlers::update_building,
96        crate::infrastructure::web::handlers::building_handlers::delete_building,
97        crate::infrastructure::web::handlers::building_handlers::export_annual_report_pdf,
98        // Payments
99        // Dépenses et factures — 17 routes entrées au contrat le 2026-09-06 (#765).
100        // Leur absence est ce qui a laissé `line_items` dériver sans que rien ne
101        // le signale : sans contrat, le frontend écrit ses types à la main.
102        crate::infrastructure::web::handlers::expense_handlers::create_expense,
103        crate::infrastructure::web::handlers::expense_handlers::get_expense,
104        crate::infrastructure::web::handlers::expense_handlers::list_expenses,
105        crate::infrastructure::web::handlers::expense_handlers::list_expenses_by_building,
106        crate::infrastructure::web::handlers::expense_handlers::mark_expense_paid,
107        crate::infrastructure::web::handlers::expense_handlers::mark_expense_overdue,
108        crate::infrastructure::web::handlers::expense_handlers::cancel_expense,
109        crate::infrastructure::web::handlers::expense_handlers::reactivate_expense,
110        crate::infrastructure::web::handlers::expense_handlers::unpay_expense,
111        crate::infrastructure::web::handlers::expense_handlers::create_invoice_draft,
112        crate::infrastructure::web::handlers::expense_handlers::update_invoice_draft,
113        crate::infrastructure::web::handlers::expense_handlers::submit_invoice_for_approval,
114        crate::infrastructure::web::handlers::expense_handlers::approve_invoice,
115        crate::infrastructure::web::handlers::expense_handlers::reject_invoice,
116        crate::infrastructure::web::handlers::expense_handlers::get_pending_invoices,
117        crate::infrastructure::web::handlers::expense_handlers::get_invoice,
118        crate::infrastructure::web::handlers::expense_handlers::export_work_quote_pdf,
119        crate::infrastructure::web::handlers::payment_handlers::create_payment,
120        crate::infrastructure::web::handlers::payment_handlers::get_payment,
121        crate::infrastructure::web::handlers::payment_handlers::get_payment_by_stripe_intent,
122        crate::infrastructure::web::handlers::payment_handlers::list_owner_payments,
123        crate::infrastructure::web::handlers::payment_handlers::list_building_payments,
124        crate::infrastructure::web::handlers::payment_handlers::list_expense_payments,
125        crate::infrastructure::web::handlers::payment_handlers::list_organization_payments,
126        crate::infrastructure::web::handlers::payment_handlers::list_payments_by_status,
127        crate::infrastructure::web::handlers::payment_handlers::list_pending_payments,
128        crate::infrastructure::web::handlers::payment_handlers::list_failed_payments,
129        crate::infrastructure::web::handlers::payment_handlers::mark_payment_processing,
130        crate::infrastructure::web::handlers::payment_handlers::mark_payment_requires_action,
131        crate::infrastructure::web::handlers::payment_handlers::mark_payment_succeeded,
132        crate::infrastructure::web::handlers::payment_handlers::mark_payment_failed,
133        crate::infrastructure::web::handlers::payment_handlers::mark_payment_cancelled,
134        crate::infrastructure::web::handlers::payment_handlers::refund_payment,
135        crate::infrastructure::web::handlers::payment_handlers::delete_payment,
136        crate::infrastructure::web::handlers::payment_handlers::get_owner_payment_stats,
137        crate::infrastructure::web::handlers::payment_handlers::get_building_payment_stats,
138        crate::infrastructure::web::handlers::payment_handlers::get_expense_total_paid,
139        crate::infrastructure::web::handlers::payment_handlers::get_owner_total_paid,
140        crate::infrastructure::web::handlers::payment_handlers::get_building_total_paid,
141        // Tickets
142        crate::infrastructure::web::handlers::ticket_handlers::create_ticket,
143        crate::infrastructure::web::handlers::ticket_handlers::get_ticket,
144        crate::infrastructure::web::handlers::ticket_handlers::update_ticket_fields,
145        crate::infrastructure::web::handlers::ticket_handlers::delete_ticket,
146        crate::infrastructure::web::handlers::ticket_handlers::list_my_tickets,
147        crate::infrastructure::web::handlers::ticket_handlers::list_assigned_tickets,
148        crate::infrastructure::web::handlers::ticket_handlers::list_building_tickets,
149        crate::infrastructure::web::handlers::ticket_handlers::list_organization_tickets,
150        crate::infrastructure::web::handlers::ticket_handlers::list_tickets_by_status,
151        crate::infrastructure::web::handlers::ticket_handlers::assign_ticket,
152        crate::infrastructure::web::handlers::ticket_handlers::start_work,
153        crate::infrastructure::web::handlers::ticket_handlers::resolve_ticket,
154        crate::infrastructure::web::handlers::ticket_handlers::close_ticket,
155        crate::infrastructure::web::handlers::ticket_handlers::cancel_ticket,
156        crate::infrastructure::web::handlers::ticket_handlers::reopen_ticket,
157        crate::infrastructure::web::handlers::ticket_handlers::get_ticket_statistics,
158        crate::infrastructure::web::handlers::ticket_handlers::get_ticket_statistics_org,
159        crate::infrastructure::web::handlers::ticket_handlers::get_overdue_tickets,
160        crate::infrastructure::web::handlers::ticket_handlers::get_overdue_tickets_org,
161        // Polls
162        crate::infrastructure::web::handlers::poll_handlers::create_poll,
163        crate::infrastructure::web::handlers::poll_handlers::get_poll,
164        crate::infrastructure::web::handlers::poll_handlers::update_poll,
165        crate::infrastructure::web::handlers::poll_handlers::list_polls,
166        crate::infrastructure::web::handlers::poll_handlers::find_active_polls,
167        crate::infrastructure::web::handlers::poll_handlers::publish_poll,
168        crate::infrastructure::web::handlers::poll_handlers::close_poll,
169        crate::infrastructure::web::handlers::poll_handlers::cancel_poll,
170        crate::infrastructure::web::handlers::poll_handlers::delete_poll,
171        crate::infrastructure::web::handlers::poll_handlers::cast_poll_vote,
172        crate::infrastructure::web::handlers::poll_handlers::get_poll_results,
173        crate::infrastructure::web::handlers::poll_handlers::get_poll_building_statistics,
174        // Resolutions
175        crate::infrastructure::web::handlers::resolution_handlers::create_resolution,
176        crate::infrastructure::web::handlers::resolution_handlers::get_resolution,
177        crate::infrastructure::web::handlers::resolution_handlers::list_meeting_resolutions,
178        crate::infrastructure::web::handlers::resolution_handlers::delete_resolution,
179        crate::infrastructure::web::handlers::resolution_handlers::cast_vote,
180        crate::infrastructure::web::handlers::resolution_handlers::list_resolution_votes,
181        crate::infrastructure::web::handlers::resolution_handlers::change_vote,
182        crate::infrastructure::web::handlers::resolution_handlers::close_voting,
183        crate::infrastructure::web::handlers::resolution_handlers::get_meeting_vote_summary,
184        // Notifications
185        crate::infrastructure::web::handlers::notification_handlers::create_notification,
186        crate::infrastructure::web::handlers::notification_handlers::get_notification,
187        crate::infrastructure::web::handlers::notification_handlers::list_my_notifications,
188        crate::infrastructure::web::handlers::notification_handlers::list_unread_notifications,
189        crate::infrastructure::web::handlers::notification_handlers::mark_notification_read,
190        crate::infrastructure::web::handlers::notification_handlers::mark_all_notifications_read,
191        crate::infrastructure::web::handlers::notification_handlers::delete_notification,
192        crate::infrastructure::web::handlers::notification_handlers::get_notification_stats,
193        crate::infrastructure::web::handlers::notification_handlers::get_user_preferences,
194        crate::infrastructure::web::handlers::notification_handlers::get_preference,
195        crate::infrastructure::web::handlers::notification_handlers::update_preference,
196        // GDPR
197        crate::infrastructure::web::handlers::gdpr_handlers::export_user_data,
198        crate::infrastructure::web::handlers::gdpr_handlers::erase_user_data,
199        crate::infrastructure::web::handlers::gdpr_handlers::can_erase_user,
200        crate::infrastructure::web::handlers::gdpr_handlers::rectify_user_data,
201        crate::infrastructure::web::handlers::gdpr_handlers::restrict_user_processing,
202        crate::infrastructure::web::handlers::gdpr_handlers::set_marketing_preference,
203        // Consent (GDPR)
204        crate::infrastructure::web::handlers::consent_handlers::record_consent,
205        crate::infrastructure::web::handlers::consent_handlers::get_consent_status,
206        // Legal Reference
207        crate::infrastructure::web::handlers::legal_handlers::list_legal_rules,
208        crate::infrastructure::web::handlers::legal_handlers::get_legal_rule,
209        crate::infrastructure::web::handlers::legal_handlers::get_ag_sequence,
210        crate::infrastructure::web::handlers::legal_handlers::get_majority_for,
211        // ContractorEvaluation (Story 3.9 — FR34 FR35 INV-21 INV-24)
212        crate::infrastructure::web::handlers::contractor_evaluation_handlers::create_contractor_evaluation,
213        crate::infrastructure::web::handlers::contractor_evaluation_handlers::get_contractor_evaluation,
214        crate::infrastructure::web::handlers::contractor_evaluation_handlers::list_contractor_evaluations,
215        // MagicLink (Story 3.2 — FR6 INV-13 INV-17)
216        crate::infrastructure::web::handlers::magic_link_handlers::issue_magic_link,
217        crate::infrastructure::web::handlers::magic_link_handlers::consume_magic_link,
218        crate::infrastructure::web::handlers::magic_link_handlers::respond_magic_link,
219        // Mandate (Story 3.4 — FR7 INV-14)
220        crate::infrastructure::web::handlers::mandate_handlers::issue_mandate,
221        crate::infrastructure::web::handlers::mandate_handlers::list_mandates,
222        crate::infrastructure::web::handlers::mandate_handlers::get_mandate,
223        crate::infrastructure::web::handlers::mandate_handlers::revoke_mandate,
224        // Users — org-scoped listing (syndic org-users-endpoint)
225        crate::infrastructure::web::handlers::user_handlers::list_organization_users,
226        // RoleAssignment — CRUD REST (Story B0bis — gap Story 3.1)
227        crate::infrastructure::web::handlers::role_assignment_handlers::assign_role,
228        crate::infrastructure::web::handlers::role_assignment_handlers::list_role_assignments_for_user,
229        crate::infrastructure::web::handlers::role_assignment_handlers::revoke_role_assignment,
230        crate::infrastructure::web::handlers::role_assignment_handlers::list_role_assignments_admin,
231        // RoleDelegation (Story 3.5 — FR8 INV-8)
232        crate::infrastructure::web::handlers::role_delegation_handlers::create_role_delegation,
233        crate::infrastructure::web::handlers::role_delegation_handlers::revoke_role_delegation,
234        crate::infrastructure::web::handlers::role_delegation_handlers::list_role_delegations,
235        // SyndicResponse (Story 3.7 — FR32 INV-23)
236        crate::infrastructure::web::handlers::syndic_response_handlers::create_syndic_response,
237        crate::infrastructure::web::handlers::syndic_response_handlers::list_syndic_responses,
238        // TechnicalSpec (Story 3.8 — FR33)
239        crate::infrastructure::web::handlers::technical_spec_handlers::create_technical_spec,
240        crate::infrastructure::web::handlers::technical_spec_handlers::bump_technical_spec,
241        crate::infrastructure::web::handlers::technical_spec_handlers::submit_technical_spec,
242        crate::infrastructure::web::handlers::technical_spec_handlers::sign_technical_spec,
243        crate::infrastructure::web::handlers::technical_spec_handlers::get_technical_spec,
244        crate::infrastructure::web::handlers::technical_spec_handlers::list_technical_specs,
245        // JournalEntries — remboursement de dette de contrat (rapport du
246        // 2026-09-01). Les 4 routes du grand livre etaient hors spec : le
247        // frontend devinait donc les noms de champs, d'ou les confusions
248        // `operation_date`/`entry_date` et `reference`/`document_ref` du
249        // constat F16. Voir `scripts/check-openapi-coverage.sh`.
250        crate::infrastructure::web::handlers::journal_entry_handlers::create_journal_entry,
251        crate::infrastructure::web::handlers::journal_entry_handlers::list_journal_entries,
252        crate::infrastructure::web::handlers::journal_entry_handlers::get_journal_entry,
253        crate::infrastructure::web::handlers::journal_entry_handlers::delete_journal_entry,
254        // OwnerContributions — quote-parts des coproprietaires (constat F4).
255        crate::infrastructure::web::handlers::owner_contribution_handlers::create_contribution,
256        crate::infrastructure::web::handlers::owner_contribution_handlers::get_contribution,
257        crate::infrastructure::web::handlers::owner_contribution_handlers::get_contributions_by_owner,
258        crate::infrastructure::web::handlers::owner_contribution_handlers::get_outstanding_contributions,
259        crate::infrastructure::web::handlers::owner_contribution_handlers::record_payment,
260        // Units — `PUT /units/{id}` acceptait `owner_id` en silence (constat F1).
261        crate::infrastructure::web::handlers::unit_handlers::create_unit,
262        crate::infrastructure::web::handlers::unit_handlers::get_unit,
263        crate::infrastructure::web::handlers::unit_handlers::list_units,
264        crate::infrastructure::web::handlers::unit_handlers::list_units_by_building,
265        crate::infrastructure::web::handlers::unit_handlers::update_unit,
266        crate::infrastructure::web::handlers::unit_handlers::delete_unit,
267        crate::infrastructure::web::handlers::unit_handlers::assign_owner,
268        // CallForFunds — la ventilation par tantiemes (constat F2).
269        crate::infrastructure::web::handlers::call_for_funds_handlers::create_call_for_funds,
270        crate::infrastructure::web::handlers::call_for_funds_handlers::get_call_for_funds,
271        crate::infrastructure::web::handlers::call_for_funds_handlers::list_call_for_funds,
272        crate::infrastructure::web::handlers::call_for_funds_handlers::get_overdue_calls,
273        crate::infrastructure::web::handlers::call_for_funds_handlers::send_call_for_funds,
274        crate::infrastructure::web::handlers::call_for_funds_handlers::cancel_call_for_funds,
275        crate::infrastructure::web::handlers::call_for_funds_handlers::delete_call_for_funds,
276        // Portfolios (Story 2.1 — portefeuille immeubles multi-rôle).
277        // Annotées depuis leur écriture, mais jamais enregistrées ici : elles
278        // n'atteignaient donc pas `docs/api/openapi.json`, et le frontend
279        // n'avait aucun type généré pour elles. C'est l'angle mort que le gate
280        // #734 ferme désormais — annoter ne suffit pas, il faut enregistrer.
281        crate::infrastructure::web::handlers::portfolio_handlers::create_portfolio,
282        crate::infrastructure::web::handlers::portfolio_handlers::list_portfolios,
283        crate::infrastructure::web::handlers::portfolio_handlers::get_portfolio,
284        crate::infrastructure::web::handlers::portfolio_handlers::update_portfolio,
285        crate::infrastructure::web::handlers::portfolio_handlers::delete_portfolio,
286        crate::infrastructure::web::handlers::portfolio_handlers::add_portfolio_building,
287        crate::infrastructure::web::handlers::portfolio_handlers::list_portfolio_buildings,
288        crate::infrastructure::web::handlers::portfolio_handlers::remove_portfolio_building,
289        crate::infrastructure::web::handlers::portfolio_handlers::share_portfolio,
290        crate::infrastructure::web::handlers::portfolio_handlers::list_portfolio_shares,
291        crate::infrastructure::web::handlers::portfolio_handlers::unshare_portfolio,
292        // Tickets — deux routes restées hors contrat pour la même raison.
293        crate::infrastructure::web::handlers::ticket_handlers::send_work_order,
294        crate::infrastructure::web::handlers::ticket_handlers::list_assignable_users,
295        // Authentification — la déconnexion manquait au contrat.
296        crate::infrastructure::web::handlers::auth_handlers::logout,
297        // Moyens de paiement (#732). Les quinze routes existaient et
298        // fonctionnaient ; aucune n'était déclarée, si bien que le frontend a
299        // écrit son DTO à la main — en oubliant `stripe_customer_id` et
300        // `is_default`, tous deux requis. D'où un 400 à chaque ajout de moyen
301        // de paiement, avec une CI verte de bout en bout.
302        crate::infrastructure::web::handlers::payment_method_handlers::create_payment_method,
303        crate::infrastructure::web::handlers::payment_method_handlers::get_payment_method,
304        crate::infrastructure::web::handlers::payment_method_handlers::get_payment_method_by_stripe_id,
305        crate::infrastructure::web::handlers::payment_method_handlers::list_owner_payment_methods,
306        crate::infrastructure::web::handlers::payment_method_handlers::list_active_owner_payment_methods,
307        crate::infrastructure::web::handlers::payment_method_handlers::get_default_payment_method,
308        crate::infrastructure::web::handlers::payment_method_handlers::list_organization_payment_methods,
309        crate::infrastructure::web::handlers::payment_method_handlers::list_payment_methods_by_type,
310        crate::infrastructure::web::handlers::payment_method_handlers::update_payment_method,
311        crate::infrastructure::web::handlers::payment_method_handlers::set_payment_method_as_default,
312        crate::infrastructure::web::handlers::payment_method_handlers::deactivate_payment_method,
313        crate::infrastructure::web::handlers::payment_method_handlers::reactivate_payment_method,
314        crate::infrastructure::web::handlers::payment_method_handlers::delete_payment_method,
315        crate::infrastructure::web::handlers::payment_method_handlers::count_active_payment_methods,
316        crate::infrastructure::web::handlers::payment_method_handlers::has_active_payment_methods,
317    ),
318    components(schemas(
319            crate::application::dto::PaymentMethodResponse,
320            crate::application::dto::CreatePaymentMethodRequest,
321            crate::application::dto::UpdatePaymentMethodRequest,
322        // JournalEntries — le `#[derive(ToSchema)]` seul NE SUFFIT PAS :
323        // utoipa ne collecte que ce qui est enregistre ici.
324        crate::infrastructure::web::handlers::journal_entry_handlers::CreateJournalEntryRequest,
325        crate::infrastructure::web::handlers::journal_entry_handlers::JournalEntryLineRequest,
326        crate::infrastructure::web::handlers::journal_entry_handlers::JournalEntryResponse,
327        crate::infrastructure::web::handlers::journal_entry_handlers::JournalEntryLineResponse,
328        crate::infrastructure::web::handlers::journal_entry_handlers::JournalEntryWithLinesResponse,
329        // OwnerContributions
330        crate::application::dto::owner_contribution_dto::CreateOwnerContributionRequest,
331        crate::application::dto::owner_contribution_dto::RecordPaymentRequest,
332        crate::application::dto::owner_contribution_dto::OwnerContributionResponse,
333        crate::domain::entities::owner_contribution::ContributionType,
334        crate::domain::entities::owner_contribution::ContributionPaymentStatus,
335        crate::domain::entities::owner_contribution::ContributionPaymentMethod,
336        // Units
337        crate::application::dto::unit_dto::CreateUnitDto,
338        crate::application::dto::unit_dto::UpdateUnitDto,
339        crate::application::dto::unit_dto::UnitResponseDto,
340        crate::domain::entities::unit::UnitType,
341        // CallForFunds
342        crate::application::dto::call_for_funds_dto::CreateCallForFundsRequest,
343        crate::application::dto::call_for_funds_dto::CallForFundsResponse,
344        crate::application::dto::call_for_funds_dto::SendCallForFundsRequest,
345        crate::application::dto::call_for_funds_dto::SendCallForFundsResponse,
346        // ACP — voir la note dans `paths()` ci-dessus.
347        crate::application::dto::acp_dto::CreateAcpDto,
348        crate::application::dto::acp_dto::UpdateAcpDto,
349        crate::application::dto::acp_dto::AcpResponseDto,
350        crate::application::dto::module_dto::EnabledModulesResponseDto,
351        crate::application::dto::board_alert_dto::CreateBoardAlertDto,
352        crate::application::dto::board_alert_dto::BoardAlertResponseDto,
353        crate::application::dto::board_alert_dto::CdcCandidateDto,
354        crate::application::dto::board_alert_dto::ElectCdcMembersDto,
355        crate::application::dto::board_member_dto::BoardMemberResponseDto,
356        crate::application::dto::convocation_dto::EligibleRecipientResponse,
357        crate::application::dto::unit_owner_dto::DesignateVotingRepresentativeDto,
358        crate::application::dto::unit_owner_dto::VotingRepresentativeResponseDto,
359        crate::application::use_cases::lien_notaire_use_cases::IssuedLienNotaireDto,
360        crate::application::use_cases::lien_notaire_use_cases::LienNotaireStatusDto,
361        crate::domain::entities::acp::AcpLegalStatus,
362        // Pagination primitives — referenced by query params on list endpoints
363        crate::application::dto::pagination::SortOrder,
364        // STORY-P7-701/702: all enums used by frontend wrappers are exposed
365        // here so `openapi-typescript` emits them into api.d.ts for re-export.
366        crate::domain::entities::resolution::ResolutionType,
367        crate::domain::entities::resolution::MajorityType,
368        crate::domain::entities::ticket::TicketCategory,
369        crate::domain::entities::ticket::TicketPriority,
370        crate::domain::entities::ticket::TicketStatus,
371        crate::domain::entities::poll::PollStatus,
372        crate::domain::entities::poll::PollType,
373        crate::domain::entities::meeting::MeetingType,
374        crate::domain::entities::meeting::MeetingStatus,
375        crate::application::dto::expense_dto::CreateExpenseDto,
376        crate::application::dto::expense_dto::ExpenseResponseDto,
377        crate::application::dto::expense_dto::CreateInvoiceDraftDto,
378        crate::application::dto::expense_dto::UpdateInvoiceDraftDto,
379        crate::application::dto::expense_dto::SubmitForApprovalDto,
380        crate::application::dto::expense_dto::ApproveInvoiceDto,
381        crate::application::dto::expense_dto::RejectInvoiceDto,
382        crate::domain::entities::expense::ExpenseCategory,
383        crate::domain::entities::expense::PaymentStatus,
384        crate::domain::entities::expense::ApprovalStatus,
385        crate::domain::entities::resource_booking::ResourceType,
386        crate::domain::entities::resource_booking::BookingStatus,
387        crate::domain::entities::resource_booking::RecurringPattern,
388        crate::domain::entities::shared_object::SharedObjectCategory,
389        crate::domain::entities::shared_object::ObjectCondition,
390        crate::domain::entities::budget::BudgetStatus,
391        crate::domain::entities::convocation::ConvocationType,
392        crate::domain::entities::convocation::ConvocationStatus,
393        crate::domain::entities::convocation_recipient::AttendanceStatus,
394        crate::domain::entities::energy_campaign::CampaignType,
395        crate::domain::entities::energy_campaign::CampaignStatus,
396        crate::domain::entities::energy_campaign::EnergyType,
397        crate::domain::entities::energy_campaign::ContractType,
398        crate::domain::entities::etat_date::EtatDateStatus,
399        crate::domain::entities::etat_date::EtatDateLanguage,
400        crate::domain::entities::achievement::AchievementCategory,
401        crate::domain::entities::achievement::AchievementTier,
402        crate::domain::entities::challenge::ChallengeStatus,
403        crate::domain::entities::challenge::ChallengeType,
404        crate::domain::entities::technical_inspection::InspectionType,
405        crate::domain::entities::technical_inspection::InspectionStatus,
406        crate::domain::entities::local_exchange::ExchangeType,
407        crate::domain::entities::local_exchange::ExchangeStatus,
408        crate::domain::entities::owner_credit_balance::CreditStatus,
409        crate::domain::entities::owner_credit_balance::ParticipationLevel,
410        crate::domain::entities::notice::NoticeType,
411        crate::domain::entities::notice::NoticeCategory,
412        crate::domain::entities::notice::NoticeStatus,
413        crate::domain::entities::payment_reminder::ReminderLevel,
414        crate::domain::entities::payment_reminder::ReminderStatus,
415        crate::domain::entities::payment_reminder::DeliveryMethod,
416        crate::domain::entities::quote::QuoteStatus,
417        crate::domain::entities::skill::SkillCategory,
418        crate::domain::entities::skill::ExpertiseLevel,
419        crate::domain::entities::work_report::WorkType,
420        crate::domain::entities::work_report::WarrantyType,
421        crate::domain::entities::resolution::ResolutionStatus,
422        crate::domain::entities::notification::NotificationStatus,
423        crate::domain::entities::notification::NotificationPriority,
424        crate::domain::entities::payment::TransactionStatus,
425        crate::domain::entities::payment_method::PaymentMethodType,
426    )),
427    modifiers(&SecurityAddon),
428    tags(
429        (name = "Health", description = "System health and monitoring"),
430        (name = "Auth", description = "Authentication and authorization"),
431        (name = "Buildings", description = "Building management"),
432        (name = "Units", description = "Unit management"),
433        (name = "Owners", description = "Owner management"),
434        (name = "Expenses", description = "Expense and invoice management"),
435        (name = "Meetings", description = "General assembly management"),
436        (name = "Budgets", description = "Annual budget management"),
437        (name = "JournalEntries", description = "Double-entry bookkeeping (PCMN general ledger)"),
438        (name = "OwnerContributions", description = "Owner quote-parts (calls for funds receivables)"),
439        (name = "CallForFunds", description = "Collective calls for funds, split by ownership shares"),
440        (name = "Documents", description = "Document upload/download"),
441        (name = "GDPR", description = "Data privacy compliance"),
442        (name = "Payments", description = "Payment processing"),
443        (name = "PaymentMethods", description = "Stored payment methods"),
444        (name = "LocalExchanges", description = "SEL time-based exchange system"),
445        (name = "Notifications", description = "Multi-channel notifications"),
446        (name = "Tickets", description = "Maintenance request system"),
447        (name = "Resolutions", description = "Meeting voting system"),
448        (name = "BoardMembers", description = "Board of directors management"),
449        (name = "Quotes", description = "Contractor quote management"),
450        (name = "EtatsDates", description = "Property sale documentation"),
451        (name = "PaymentRecovery", description = "Automated payment reminders"),
452        (name = "Consent", description = "User consent management (GDPR Art. 7)"),
453        (name = "Legal Reference", description = "Belgian legal reference rules and majority types"),
454        (name = "ContractorEvaluation", description = "Contractor performance evaluations (Story 3.9 — FR34 FR35 INV-21 INV-24)"),
455        (name = "MagicLink", description = "Single-use scoped magic links for public access (Story 3.2 — FR6 INV-13 INV-17)"),
456        (name = "Mandate", description = "Time-bounded mandates for mandataire delegation (Story 3.4 — FR7 INV-14)"),
457        (name = "RoleAssignment", description = "CRUD REST on user_role_assignments (Story B0bis — gap fill Story 3.1)"),
458        (name = "RoleDelegation", description = "Temporary role delegation between users (Story 3.5 — FR8 INV-8)"),
459        (name = "SyndicResponse", description = "Append-only syndic replies to tickets (Story 3.7 — FR32 INV-23)"),
460        (name = "TechnicalSpec", description = "Versionable technical specifications with multi-party signatures (Story 3.8 — FR33)"),
461    )
462)]
463pub struct ApiDoc;
464
465/// Add JWT Bearer authentication to OpenAPI spec
466struct SecurityAddon;
467
468impl Modify for SecurityAddon {
469    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
470        if let Some(components) = openapi.components.as_mut() {
471            components.add_security_scheme(
472                "bearer_auth",
473                SecurityScheme::Http(
474                    HttpBuilder::new()
475                        .scheme(HttpAuthScheme::Bearer)
476                        .bearer_format("JWT")
477                        .description(Some(
478                            "JWT token obtained from /api/v1/auth/login.\n\n\
479                            Example: `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`\n\n\
480                            To authenticate:\n\
481                            1. Click 'Authorize' button above\n\
482                            2. Enter token (with or without 'Bearer ' prefix)\n\
483                            3. Click 'Authorize' in dialog\n\
484                            4. Try endpoints",
485                        ))
486                        .build(),
487                ),
488            )
489        }
490    }
491}
492
493/// Configure Swagger UI service
494///
495/// Swagger UI will be available at: http://localhost:8080/swagger-ui/
496pub fn configure_swagger_ui() -> SwaggerUi {
497    SwaggerUi::new("/swagger-ui/{_:.*}")
498        .url("/api-docs/openapi.json", ApiDoc::openapi())
499        .config(
500            utoipa_swagger_ui::Config::default()
501                .try_it_out_enabled(true)
502                .persist_authorization(true)
503                .display_request_duration(true)
504                .deep_linking(true)
505                .display_operation_id(true)
506                .default_models_expand_depth(1)
507                .default_model_expand_depth(1), // .doc_expansion(utoipa_swagger_ui::DocExpansion::List) // Removed: DocExpansion no longer exists in utoipa_swagger_ui
508        )
509}
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514
515    #[test]
516    fn test_openapi_spec_generation() {
517        let spec = ApiDoc::openapi();
518
519        // Verify basic info
520        assert_eq!(spec.info.title, "KoproGo API");
521        assert_eq!(spec.info.version, "1.0.0");
522
523        // Verify servers
524        assert!(spec.servers.is_some());
525        let servers = spec.servers.unwrap();
526        assert_eq!(servers.len(), 2);
527
528        // Verify security scheme
529        assert!(spec.components.is_some());
530        let components = spec.components.unwrap();
531        assert!(components.security_schemes.contains_key("bearer_auth"));
532
533        // Verify tags
534        assert!(spec.tags.is_some());
535        let tags = spec.tags.unwrap();
536        assert!(tags.len() >= 15);
537    }
538
539    #[test]
540    fn test_swagger_ui_configuration() {
541        let _swagger = configure_swagger_ui();
542        // SwaggerUi is configured, this test ensures it compiles
543    }
544
545    #[test]
546    fn test_openapi_json_is_valid() {
547        let spec = ApiDoc::openapi();
548
549        // Serialize to JSON to ensure it's valid
550        let json = serde_json::to_string(&spec).expect("Should serialize to JSON");
551        assert!(json.contains("\"title\":\"KoproGo API\""));
552        assert!(json.contains("\"version\":\"1.0.0\""));
553    }
554}