""" Versioning API endpoints for ThrillWiki. Provides REST API for: - Version history for entities - Specific version details - Comparing versions - Diff with current state - Version restoration (optional) """ from typing import List from uuid import UUID from django.shortcuts import get_object_or_404 from django.http import Http404 from ninja import Router from apps.entities.models import Park, Ride, Company, RideModel from apps.versioning.models import EntityVersion from apps.versioning.services import VersionService from api.v1.schemas import ( EntityVersionSchema, VersionHistoryResponseSchema, VersionDiffSchema, VersionComparisonSchema, ErrorSchema, MessageSchema ) router = Router(tags=['Versioning']) # Park Versions @router.get( '/parks/{park_id}/versions', response={200: VersionHistoryResponseSchema, 404: ErrorSchema}, summary="Get park version history" ) def get_park_versions(request, park_id: UUID, limit: int = 50): """ Get version history for a park. Returns up to `limit` versions in reverse chronological order (newest first). """ park = get_object_or_404(Park, id=park_id) versions = VersionService.get_version_history(park, limit=limit) return { 'entity_id': str(park.id), 'entity_type': 'park', 'entity_name': park.name, 'total_versions': VersionService.get_version_count(park), 'versions': [ EntityVersionSchema.from_orm(v) for v in versions ] } @router.get( '/parks/{park_id}/versions/{version_number}', response={200: EntityVersionSchema, 404: ErrorSchema}, summary="Get specific park version" ) def get_park_version(request, park_id: UUID, version_number: int): """Get a specific version of a park by version number.""" park = get_object_or_404(Park, id=park_id) version = VersionService.get_version_by_number(park, version_number) if not version: raise Http404("Version not found") return EntityVersionSchema.from_orm(version) @router.get( '/parks/{park_id}/versions/{version_number}/diff', response={200: VersionDiffSchema, 404: ErrorSchema}, summary="Compare park version with current" ) def get_park_version_diff(request, park_id: UUID, version_number: int): """ Compare a specific version with the current park state. Returns the differences between the version and current values. """ park = get_object_or_404(Park, id=park_id) version = VersionService.get_version_by_number(park, version_number) if not version: raise Http404("Version not found") diff = VersionService.get_diff_with_current(version) return { 'entity_id': str(park.id), 'entity_type': 'park', 'entity_name': park.name, 'version_number': version.version_number, 'version_date': version.created, 'differences': diff['differences'], 'changed_field_count': diff['changed_field_count'] } # Ride Versions @router.get( '/rides/{ride_id}/versions', response={200: VersionHistoryResponseSchema, 404: ErrorSchema}, summary="Get ride version history" ) def get_ride_versions(request, ride_id: UUID, limit: int = 50): """Get version history for a ride.""" ride = get_object_or_404(Ride, id=ride_id) versions = VersionService.get_version_history(ride, limit=limit) return { 'entity_id': str(ride.id), 'entity_type': 'ride', 'entity_name': ride.name, 'total_versions': VersionService.get_version_count(ride), 'versions': [ EntityVersionSchema.from_orm(v) for v in versions ] } @router.get( '/rides/{ride_id}/versions/{version_number}', response={200: EntityVersionSchema, 404: ErrorSchema}, summary="Get specific ride version" ) def get_ride_version(request, ride_id: UUID, version_number: int): """Get a specific version of a ride by version number.""" ride = get_object_or_404(Ride, id=ride_id) version = VersionService.get_version_by_number(ride, version_number) if not version: raise Http404("Version not found") return EntityVersionSchema.from_orm(version) @router.get( '/rides/{ride_id}/versions/{version_number}/diff', response={200: VersionDiffSchema, 404: ErrorSchema}, summary="Compare ride version with current" ) def get_ride_version_diff(request, ride_id: UUID, version_number: int): """Compare a specific version with the current ride state.""" ride = get_object_or_404(Ride, id=ride_id) version = VersionService.get_version_by_number(ride, version_number) if not version: raise Http404("Version not found") diff = VersionService.get_diff_with_current(version) return { 'entity_id': str(ride.id), 'entity_type': 'ride', 'entity_name': ride.name, 'version_number': version.version_number, 'version_date': version.created, 'differences': diff['differences'], 'changed_field_count': diff['changed_field_count'] } # Company Versions @router.get( '/companies/{company_id}/versions', response={200: VersionHistoryResponseSchema, 404: ErrorSchema}, summary="Get company version history" ) def get_company_versions(request, company_id: UUID, limit: int = 50): """Get version history for a company.""" company = get_object_or_404(Company, id=company_id) versions = VersionService.get_version_history(company, limit=limit) return { 'entity_id': str(company.id), 'entity_type': 'company', 'entity_name': company.name, 'total_versions': VersionService.get_version_count(company), 'versions': [ EntityVersionSchema.from_orm(v) for v in versions ] } @router.get( '/companies/{company_id}/versions/{version_number}', response={200: EntityVersionSchema, 404: ErrorSchema}, summary="Get specific company version" ) def get_company_version(request, company_id: UUID, version_number: int): """Get a specific version of a company by version number.""" company = get_object_or_404(Company, id=company_id) version = VersionService.get_version_by_number(company, version_number) if not version: raise Http404("Version not found") return EntityVersionSchema.from_orm(version) @router.get( '/companies/{company_id}/versions/{version_number}/diff', response={200: VersionDiffSchema, 404: ErrorSchema}, summary="Compare company version with current" ) def get_company_version_diff(request, company_id: UUID, version_number: int): """Compare a specific version with the current company state.""" company = get_object_or_404(Company, id=company_id) version = VersionService.get_version_by_number(company, version_number) if not version: raise Http404("Version not found") diff = VersionService.get_diff_with_current(version) return { 'entity_id': str(company.id), 'entity_type': 'company', 'entity_name': company.name, 'version_number': version.version_number, 'version_date': version.created, 'differences': diff['differences'], 'changed_field_count': diff['changed_field_count'] } # Ride Model Versions @router.get( '/ride-models/{model_id}/versions', response={200: VersionHistoryResponseSchema, 404: ErrorSchema}, summary="Get ride model version history" ) def get_ride_model_versions(request, model_id: UUID, limit: int = 50): """Get version history for a ride model.""" model = get_object_or_404(RideModel, id=model_id) versions = VersionService.get_version_history(model, limit=limit) return { 'entity_id': str(model.id), 'entity_type': 'ride_model', 'entity_name': str(model), 'total_versions': VersionService.get_version_count(model), 'versions': [ EntityVersionSchema.from_orm(v) for v in versions ] } @router.get( '/ride-models/{model_id}/versions/{version_number}', response={200: EntityVersionSchema, 404: ErrorSchema}, summary="Get specific ride model version" ) def get_ride_model_version(request, model_id: UUID, version_number: int): """Get a specific version of a ride model by version number.""" model = get_object_or_404(RideModel, id=model_id) version = VersionService.get_version_by_number(model, version_number) if not version: raise Http404("Version not found") return EntityVersionSchema.from_orm(version) @router.get( '/ride-models/{model_id}/versions/{version_number}/diff', response={200: VersionDiffSchema, 404: ErrorSchema}, summary="Compare ride model version with current" ) def get_ride_model_version_diff(request, model_id: UUID, version_number: int): """Compare a specific version with the current ride model state.""" model = get_object_or_404(RideModel, id=model_id) version = VersionService.get_version_by_number(model, version_number) if not version: raise Http404("Version not found") diff = VersionService.get_diff_with_current(version) return { 'entity_id': str(model.id), 'entity_type': 'ride_model', 'entity_name': str(model), 'version_number': version.version_number, 'version_date': version.created, 'differences': diff['differences'], 'changed_field_count': diff['changed_field_count'] } # Generic Version Endpoints @router.get( '/versions/{version_id}', response={200: EntityVersionSchema, 404: ErrorSchema}, summary="Get version by ID" ) def get_version(request, version_id: UUID): """Get a specific version by its ID.""" version = get_object_or_404(EntityVersion, id=version_id) return EntityVersionSchema.from_orm(version) @router.get( '/versions/{version_id}/compare/{other_version_id}', response={200: VersionComparisonSchema, 404: ErrorSchema}, summary="Compare two versions" ) def compare_versions(request, version_id: UUID, other_version_id: UUID): """ Compare two versions of the same entity. Both versions must be for the same entity. """ version1 = get_object_or_404(EntityVersion, id=version_id) version2 = get_object_or_404(EntityVersion, id=other_version_id) comparison = VersionService.compare_versions(version1, version2) return { 'version1': EntityVersionSchema.from_orm(version1), 'version2': EntityVersionSchema.from_orm(version2), 'differences': comparison['differences'], 'changed_field_count': comparison['changed_field_count'] } # Optional: Version Restoration # Uncomment if you want to enable version restoration via API # @router.post( # '/versions/{version_id}/restore', # response={200: MessageSchema, 404: ErrorSchema}, # summary="Restore a version" # ) # def restore_version(request, version_id: UUID): # """ # Restore an entity to a previous version. # # This creates a new version with change_type='restored'. # Requires authentication and appropriate permissions. # """ # version = get_object_or_404(EntityVersion, id=version_id) # # # Check authentication # if not request.user.is_authenticated: # return 401, {'error': 'Authentication required'} # # # Restore version # restored_version = VersionService.restore_version( # version, # user=request.user, # comment='Restored via API' # ) # # return { # 'message': f'Successfully restored to version {version.version_number}', # 'new_version_number': restored_version.version_number # }