Skip to content

Migrate HTTP Cloud Functions endpoints to FastAPI - #100

Merged
SuperMuel merged 3 commits into
mainfrom
migrate-http-cloud-functions-to-fastapi
Oct 22, 2025
Merged

SuperMuel merged 3 commits into
mainfrom
migrate-http-cloud-functions-to-fastapi

Conversation

@SuperMuel

@SuperMuel SuperMuel commented Oct 22, 2025

Copy link
Copy Markdown
Owner

Summary

  • build shared domain service container during FastAPI lifespan and inject via dependencies
  • expose FastAPI routes for calendar authorization, sync profile management, and ICS validation
  • translate domain exceptions into FastAPI HTTP errors using the existing mapping logic

Summary by CodeRabbit

  • New Features

    • Added API endpoints for calendar listing, authorization checks, sync requests, and sync profile management.
  • Refactor

    • Reorganized backend into a domain services layer with dependency-injected services and centralized error handling to standardize behavior.
  • Bug Fixes / Improvements

    • Improved error-to-HTTP mapping for clearer responses and added initialization logging for backend repositories.
  • Tests / Chores

    • Added test fixtures to ensure deterministic environment settings; removed mock defaults for sensitive configuration.

@coderabbitai

coderabbitai Bot commented Oct 22, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces a DomainServices dataclass and dependency, replaces global service usage with dependency-injected services, adds domain-aware error handling and several calendar/authorization/sync endpoints, and adds initialization logging in multiple Firestore repository classes. Settings and test env defaults were adjusted.

Changes

Cohort / File(s) Summary
DomainServices + API endpoints
backend/backend/api.py
Added DomainServices dataclass, build_domain_services() builder, get_domain_services() dependency. Reworked startup/shutdown to attach services to app.state. Replaced global service usage with services: DomainServices dependency across endpoints. Added domain-aware error handler and syncademic_error_response. Added endpoints: POST /calendars/list, POST /authorization/status, POST /sync/request, DELETE /sync-profiles/{sync_profile_id}, POST /authorization/backend, POST /sync-profiles, and updated validate_ics_url_endpoint.
Error mapping and exceptions
backend/backend/services/exceptions/mapping.py
Introduced FIREBASE_TO_FASTAPI_STATUS mapping. Renamed/expanded to_status_code_and_messageto_firebase_status_code_and_message, added to_fastapi_status_code_and_message, broadened domain error coverage and adjusted ErrorMapping.to_http_error to use the new flow.
Repository init logging
backend/backend/repositories/backend_authorization_repository.py, backend/backend/repositories/sync_profile_repository.py, backend/backend/repositories/sync_stats_repository.py
Added module-level logger and initialization logs in __init__ to record class name and Firebase project id. No API behavioral changes.
Settings defaults changed
backend/backend/settings.py
Replaced concrete mock defaults with required placeholders (...) for CLIENT_SECRET, OPENAI_API_KEY, and FIREBASE_STORAGE_BUCKET.
Test environment fixture
backend/tests/conftest.py
New test fixture setting deterministic env vars (FIREBASE_STORAGE_BUCKET, CLIENT_SECRET, OPENAI_API_KEY) using os.environ.setdefault and an autouse pytest fixture that monkeypatches these for tests.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant FastAPI as API
    participant DI as get_domain_services()
    participant AppState as app.state.DomainServices
    participant DS as DomainServices
    participant Calendar as GoogleCalendarService
    participant Auth as AuthorizationService
    participant Sync as SyncProfileService

    Client->>API: POST /calendars/list
    API->>DI: resolve dependency
    DI->>AppState: read DomainServices
    AppState-->>DI: DomainServices instance
    DI-->>API: services

    API->>Calendar: services.google_calendar_service.list_calendars(...)
    Calendar-->>API: result
    API-->>Client: 200 OK (calendars)

    alt Service raises SyncademicError
        Calendar-->>API: SyncademicError
        API->>API: syncademic_error_response(error, ERROR_MAPPING)
        API-->>Client: HTTP error (mapped status + body)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I stitched the services into one chest,
Endpoints now ask for what they need best,
Errors translate from Firebase to REST,
Logs whisper project ids at each request,
Hopping on—this rabbit calls it progress! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately captures the primary change of migrating the existing HTTP Cloud Functions endpoints to FastAPI, reflecting the core migration work done in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 90.48% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 313b3f7 and ef5bcf1.

📒 Files selected for processing (4)
  • backend/backend/api.py (8 hunks)
  • backend/backend/repositories/backend_authorization_repository.py (3 hunks)
  • backend/backend/settings.py (2 hunks)
  • backend/tests/conftest.py (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @SuperMuel, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant architectural change by migrating existing HTTP Cloud Functions logic to a FastAPI framework. The primary objective is to harness FastAPI's powerful capabilities for API development, such as dependency injection and structured error handling. This refactoring centralizes the management of backend domain services, enhancing their accessibility and testability across various API endpoints, and establishes new routes for core application functionalities.

Highlights

  • FastAPI Migration: The core logic for HTTP Cloud Functions endpoints has been migrated to FastAPI, leveraging its modern features for API development.
  • Centralized Service Management: A new DomainServices dataclass and FastAPI's lifespan event are used to build and manage all backend domain services, making them available via FastAPI's dependency injection system.
  • New FastAPI Endpoints: Several new API routes have been added for critical functionalities, including calendar authorization, sync profile management (creation, deletion, and sync requests), and ICS URL validation.
  • Standardized Error Handling: A robust mechanism has been implemented to translate custom SyncademicError exceptions into standard FastAPI HTTPExceptions, ensuring consistent and appropriate HTTP status codes for API responses.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov-commenter

codecov-commenter commented Oct 22, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 20.56075% with 85 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.96%. Comparing base (5ce6ee8) to head (ef5bcf1).

Files with missing lines Patch % Lines
backend/backend/api.py 0.00% 81 Missing ⚠️
backend/backend/services/exceptions/mapping.py 69.23% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #100      +/-   ##
==========================================
- Coverage   72.31%   69.96%   -2.36%     
==========================================
  Files          43       43              
  Lines        1994     2081      +87     
==========================================
+ Hits         1442     1456      +14     
- Misses        552      625      +73     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request does an excellent job of migrating several HTTP Cloud Functions endpoints to a FastAPI application. The implementation follows modern FastAPI best practices, such as using a dependency-injected domain service container initialized during the application's lifespan and centralizing domain exception handling. The new structure is clean and significantly improves the architecture. I've added a couple of suggestions to further enhance maintainability by reducing code duplication.

Comment thread backend/backend/api.py Outdated
Comment on lines +330 to +336
try:
calendars = services.google_calendar_service.list_calendars(
user_id=current_user.uid,
provider_account_id=payload.provider_account_id,
)
except SyncademicError as exc:
raise_syncademic_http_error(exc, error_mapping=services.error_mapping)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This try...except block for handling SyncademicError is repeated across multiple new endpoints. To reduce boilerplate and centralize error handling, consider using a FastAPI exception handler.

You could define a handler like this:

from fastapi.responses import JSONResponse

@app.exception_handler(SyncademicError)
async def syncademic_exception_handler(request: Request, exc: SyncademicError):
    services: DomainServices | None = getattr(request.app.state, "domain_services", None)
    if not services:
        # Fallback if services are not available for some reason
        return JSONResponse(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            content={"message": "Domain services are not available"},
        )

    status_code, message = services.error_mapping.to_fastapi_status_code_and_message(exc)
    
    detail: dict[str, Any] = {"message": message}
    if exc.details:
        detail["details"] = exc.details

    logger.error(
        "Syncademic error mapped to HTTP response.",
        extra={
            "error_type": type(exc).__name__,
            "status_code": status_code,
        },
    )
    return JSONResponse(
        status_code=status_code,
        content=detail,
    )

This would allow you to simplify endpoints like list_user_calendars_endpoint to be more focused on the business logic, without the repetitive try...except block.

Comment on lines 55 to +64
self._db = db or firestore.Client()
firebase_project_id = getattr(self._db, "project", None)
logger.info(
"Initialized %s with Firebase project: %s",
self.__class__.__name__,
firebase_project_id,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This initialization logic, including setting self._db and logging the project ID, is duplicated across FirestoreBackendAuthorizationRepository, FirestoreSyncProfileRepository, and FirestoreSyncStatsRepository.

To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, you could extract this common logic into a base class.

For example:

class BaseFirestoreRepository:
    def __init__(self, db: firestore.Client | None = None):
        self._db = db or firestore.Client()
        firebase_project_id = getattr(self._db, "project", None)
        logger.info(
            "Initialized %s with Firebase project: %s",
            self.__class__.__name__,
            firebase_project_id,
        )

class FirestoreBackendAuthorizationRepository(BaseFirestoreRepository, IBackendAuthorizationRepository):
    def __init__(self, db: firestore.Client | None = None):
        super().__init__(db)
    
    # ... repository methods

This would centralize the initialization logic, making it easier to manage and modify in the future.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

bucket = storage.bucket(settings.FIREBASE_STORAGE_BUCKET)
ics_file_storage = FirebaseIcsFileStorage(bucket=bucket)

P1 Badge Require explicit bucket and break default Firebase storage

The new build_domain_services constructs the cache storage with storage.bucket(settings.FIREBASE_STORAGE_BUCKET). FIREBASE_STORAGE_BUCKET defaults to "mock-storage-bucket", so unless a real bucket name is injected via configuration the FastAPI app will always point to a non‑existent bucket. The previous Cloud Functions code relied on storage.bucket() without a name, which automatically bound to the default bucket from the initialized Firebase app and worked without extra env vars. With this change, deployments that previously relied on the default bucket will now fail to read/write calendar data. Consider falling back to the Firebase default when the setting is missing instead of forcing a name.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
backend/backend/repositories/sync_stats_repository.py (1)

65-67: UTC mismatch: date.today() ignores UTC and can miscount near midnight.

Docstrings state UTC, but date.today() uses server local time. Use UTC date to avoid off‑by‑one errors.

Apply:

-from datetime import date
+from datetime import date, datetime, timezone
...
-        if day is None:
-            day = date.today()
+        if day is None:
+            day = datetime.now(timezone.utc).date()
...
-        if day is None:
-            day = date.today()
+        if day is None:
+            day = datetime.now(timezone.utc).date()

Also applies to: 91-93

backend/backend/repositories/backend_authorization_repository.py (1)

72-72: Doc ID collisions: concatenating IDs without a delimiter can corrupt data.

user_id + provider_account_id risks collisions (e.g., "ab"+"c" == "a"+"bc"). Use a delimiter or namespaced path.

Apply:

 class FirestoreBackendAuthorizationRepository(IBackendAuthorizationRepository):
+    def _doc_id(self, user_id: str, provider_account_id: str) -> str:
+        # Delimiter prevents collisions; encode if delimiter can appear in inputs.
+        return f"{user_id}:{provider_account_id}"
@@
-        doc_id = user_id + provider_account_id
+        doc_id = self._doc_id(user_id, provider_account_id)
@@
-        doc_id = backend_auth.user_id + backend_auth.provider_account_id
+        doc_id = self._doc_id(backend_auth.user_id, backend_auth.provider_account_id)
@@
-        doc_id = user_id + provider_account_id
+        doc_id = self._doc_id(user_id, provider_account_id)
@@
-        doc_id = user_id + provider_account_id
+        doc_id = self._doc_id(user_id, provider_account_id)

Also applies to: 86-86, 95-95, 104-104

backend/backend/api.py (3)

258-266: Critical: Root endpoint leaks full settings, including secrets.

Returning settings.model_dump() can expose secrets/keys. Remove or strictly sanitize.

Apply:

 @app.get("/")
 async def root() -> dict[str, Any]:
     """Root endpoint providing basic API information and available endpoints."""
     return {
         "name": "Syncademic API",
         "version": "0.1.0",
         "endpoints": {"health": "/health", "docs": "/docs", "redoc": "/redoc"},
-        "settings": settings.model_dump(),
+        # Expose only non-sensitive, high-level metadata if needed.
+        "env": settings.ENV,
     }

If you must expose config, build an explicit allowlist of non-sensitive fields. As per coding guidelines.


301-304: Avoid logging full ICS URLs (may contain secrets).

URLs can embed tokens/query params. Log a redacted form.

Apply:

-    logger.info(
-        "Validating ICS URL via FastAPI",
-        extra={"user_id": current_user.uid, "url": payload.url},
-    )
+    from urllib.parse import urlparse
+    parsed = urlparse(payload.url)
+    redacted_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
+    logger.info(
+        "Validating ICS URL via FastAPI",
+        extra={"user_id": current_user.uid, "url": redacted_url},
+    )

Also consider dropping user_id or hashing it when logging. Based on coding guidelines.


94-149: Initialize Firebase Admin SDK before accessing storage.bucket
Add a call to firebase_admin.initialize_app(cred, {"storageBucket": settings.FIREBASE_STORAGE_BUCKET}) immediately after credentials are configured (around lines 68–76) so that storage.bucket(...) won’t fail. Also ensure FIREBASE_STORAGE_BUCKET is overridden in production (default is "mock-storage-bucket").

🧹 Nitpick comments (7)
backend/backend/repositories/sync_stats_repository.py (1)

50-55: Init logging is fine; consider structured field for project id.

Keep the message, but also attach firebase_project_id via extra for better queryability in structured logs.

Apply:

-        logger.info(
-            "Initialized %s with Firebase project: %s",
-            self.__class__.__name__,
-            firebase_project_id,
-        )
+        logger.info(
+            "Initialized %s with Firebase project: %s",
+            self.__class__.__name__,
+            firebase_project_id,
+            extra={"firebase_project_id": firebase_project_id},
+        )
backend/backend/repositories/sync_profile_repository.py (1)

98-102: Reduce PII/log noise in repository reads.

logger.info for every read with user_id and sync_profile_id can be noisy and leaks identifiers. Prefer debug and/or hash/truncate IDs.

Apply:

-        logger.info(
+        logger.debug(
             "Getting sync profile %s for user %s",
             sync_profile_id,
             user_id,
         )
backend/backend/repositories/backend_authorization_repository.py (1)

41-43: Nit: typo in docstring.
“acutally” -> “actually”.

-        and provider_account_id, otherwise False. It doesn't acutally check if the token is expired or
+        and provider_account_id, otherwise False. It doesn't actually check if the token is expired or
backend/backend/services/exceptions/mapping.py (2)

30-36: Broaden docstrings to reflect dual environments.

Class/docstrings still mention only Firebase Functions though this module now serves FastAPI too.

Apply:

-class ErrorMapping:
-    """Maps domain exceptions to Firebase Functions error codes"""
+class ErrorMapping:
+    """Maps domain exceptions to Firebase Functions codes and FastAPI HTTP statuses"""

1-1: Decouple from firebase_functions in exception mapping
In backend/backend/services/exceptions/mapping.py, replace from firebase_functions import https_fn with a minimal internal HTTP status enum and map to https_fn only at the edge. firebase_functions is already pinned in backend/uv.lock and installed by your Dockerfile, so this refactor won’t break deployments.

backend/backend/api.py (2)

314-339: Prefer Pydantic response models over plain dicts.

For consistency and schema clarity, define outputs (e.g., ListUserCalendarsOutput, OperationResult) instead of dict[str, Any]/dict[str, bool].

Example:

-@app.post("/calendars/list")
-def list_user_calendars_endpoint(...) -> dict[str, Any]:
+from backend.models.schemas import ListUserCalendarsOutput
+@app.post("/calendars/list", response_model=ListUserCalendarsOutput)
+def list_user_calendars_endpoint(... ) -> ListUserCalendarsOutput:
@@
-    return {"calendars": calendars}
+    return ListUserCalendarsOutput(calendars=calendars)

Apply similar response_model patterns to /sync/request, /sync-profiles/{id}, and /authorization/backend.

Also applies to: 377-405, 407-432, 434-462


224-245: Include error details in logs for triage.

You already attach type and status; include error.details length or keys when present for faster debugging (without logging sensitive values).

Apply:

-    logger.error(
+    logger.error(
         "Syncademic error mapped to HTTP response.",
         extra={
             "error_type": type(error).__name__,
             "status_code": status_code,
+            "has_details": bool(getattr(error, "details", {})),
+            "detail_keys": list(getattr(error, "details", {}).keys()) if getattr(error, "details", {}) else [],
         },
     )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ce6ee8 and 313b3f7.

📒 Files selected for processing (5)
  • backend/backend/api.py (7 hunks)
  • backend/backend/repositories/backend_authorization_repository.py (2 hunks)
  • backend/backend/repositories/sync_profile_repository.py (1 hunks)
  • backend/backend/repositories/sync_stats_repository.py (2 hunks)
  • backend/backend/services/exceptions/mapping.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
backend/**/*.py

📄 CodeRabbit inference engine (backend/AGENTS.md)

backend/**/*.py: Prefer Pydantic models over raw dictionaries for input validation
Favor functional, declarative style with pure functions and immutable data structures where practical
Handle errors and edge cases at the beginning of functions
Use early returns for error conditions to avoid deeply nested if statements
Place the happy path last in the function for improved readability
Avoid unnecessary else statements; use the if-return pattern instead
Use guard clauses to handle preconditions and invalid states early
Implement proper error logging and user-friendly error messages
Use assert to catch developer errors
Use docstrings for all public functions, methods, and classes
Write docstrings for public APIs that explain usage and purpose, not implementation
Focus docstrings on why to use and how to use correctly
Skip docstrings for trivial functions where name and type hints are self-explanatory
Prefer self-documenting code over comments; add comments for complex business logic or why-decisions
Use type hints throughout the codebase
Prefer built-in typing syntax like list[str | None] over List[Optional[str]]
Write testable code by minimizing patching and favoring dependency injection

Files:

  • backend/backend/repositories/backend_authorization_repository.py
  • backend/backend/repositories/sync_stats_repository.py
  • backend/backend/services/exceptions/mapping.py
  • backend/backend/repositories/sync_profile_repository.py
  • backend/backend/api.py
🧬 Code graph analysis (3)
backend/backend/repositories/backend_authorization_repository.py (1)
backend/backend/logging_config.py (1)
  • FirebaseFunctionsHandler (20-45)
backend/backend/services/exceptions/mapping.py (6)
backend/backend/services/exceptions/auth.py (3)
  • BaseAuthorizationError (4-7)
  • ProviderUserIdMismatchError (16-25)
  • UnauthorizedError (10-13)
backend/backend/services/exceptions/base.py (1)
  • SyncademicError (4-17)
backend/backend/services/exceptions/ics.py (1)
  • IcsSourceError (10-13)
backend/backend/services/exceptions/ruleset.py (2)
  • RulesetGenerationError (10-13)
  • RulesetValidationError (16-19)
backend/backend/services/exceptions/sync.py (3)
  • DailySyncLimitExceededError (16-19)
  • SyncInProgressError (22-25)
  • SyncProfileNotFoundError (10-13)
backend/backend/services/exceptions/target_calendar.py (2)
  • TargetCalendarAccessError (16-19)
  • TargetCalendarNotFoundError (10-13)
backend/backend/api.py (12)
backend/backend/ai/ruleset_builder.py (1)
  • RulesetBuilder (63-139)
backend/backend/models/sync_profile.py (1)
  • SyncTrigger (56-66)
backend/backend/models/base.py (2)
  • CamelCaseModel (7-23)
  • model_dump (15-18)
backend/backend/models/schemas.py (9)
  • AuthorizeBackendInput (74-99)
  • CreateSyncProfileInput (139-154)
  • DeleteSyncProfileInput (65-68)
  • IsAuthorizedInput (33-36)
  • IsAuthorizedOutput (39-40)
  • ListUserCalendarsInput (27-30)
  • RequestSyncInput (58-62)
  • ValidateIcsUrlInput (10-11)
  • ValidateIcsUrlOutput (14-24)
backend/backend/repositories/backend_authorization_repository.py (1)
  • FirestoreBackendAuthorizationRepository (48-115)
backend/backend/services/ai_ruleset_service.py (1)
  • AiRulesetService (17-136)
backend/backend/services/authorization_service.py (3)
  • AuthorizationService (29-282)
  • test_authorization (238-270)
  • authorize_backend_with_auth_code (49-153)
backend/backend/services/exceptions/base.py (1)
  • SyncademicError (4-17)
backend/backend/services/exceptions/mapping.py (2)
  • ErrorMapping (30-93)
  • to_fastapi_status_code_and_message (79-93)
backend/backend/services/google_calendar_service.py (2)
  • GoogleCalendarService (13-147)
  • list_calendars (17-64)
backend/backend/services/ics_service.py (2)
  • IcsService (27-172)
  • validate_ics_url (154-172)
backend/backend/services/sync_profile_service.py (4)
  • SyncProfileService (39-620)
  • synchronize (83-201)
  • delete_sync_profile (342-426)
  • create_sync_profile (428-620)
🔇 Additional comments (3)
backend/backend/repositories/backend_authorization_repository.py (1)

59-64: Init logging LGTM.
Consistent with other repos; no issues.

backend/backend/services/exceptions/mapping.py (1)

19-27: FastAPI mapping addition looks correct.

Status translations align with typical HTTP equivalents; helper cleanly reuses Firebase mapping.

Also applies to: 78-93

backend/backend/api.py (1)

85-92: DomainServices container LGTM.
Clear DI surface; aligns with guidelines to favor DI and testability.

Comment on lines +75 to +80
firebase_project_id = getattr(self._db, "project", None)
logger.info(
"Initialized %s with Firebase project: %s",
self.__class__.__name__,
firebase_project_id,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect Firestore import and client usage; will break at runtime.

from firebase_admin.firestore import firestore does not expose Client; calling firestore.Client() will fail. Use google.cloud.firestore.Client or firebase_admin.firestore.client() consistently.

Apply:

-from firebase_admin.firestore import firestore
+from google.cloud import firestore
@@
-        self._db = db or firestore.Client()
+        self._db = db or firestore.Client()

Alternatively (if you prefer firebase_admin helper):

-from firebase_admin.firestore import firestore
+from firebase_admin import firestore
@@
-        self._db = db or firestore.Client()
+        self._db = db or firestore.client()

Pick one approach and keep it consistent with other repositories (e.g., sync_stats_repository uses google.cloud.firestore). Based on learnings.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
firebase_project_id = getattr(self._db, "project", None)
logger.info(
"Initialized %s with Firebase project: %s",
self.__class__.__name__,
firebase_project_id,
)
++ b/backend/backend/repositories/sync_profile_repository.py
@@ -1,7 +1,7 @@
from google.cloud import firestore
class SyncProfileRepository:
def __init__(self, db: firestore.Client = None):
self._db = db or firestore.Client()
firebase_project_id = getattr(self._db, "project", None)
logger.info(
"Initialized %s with Firebase project: %s",
🤖 Prompt for AI Agents
In backend/backend/repositories/sync_profile_repository.py around lines 75 to
80, the code imports firestore from firebase_admin and calls firestore.Client(),
which is invalid at runtime; replace the import and client instantiation to use
the same approach as other repos (prefer google.cloud.firestore.Client) or use
the firebase_admin helper consistently. Update imports to: from google.cloud
import firestore and instantiate client = firestore.Client(), or alternatively
import firebase_admin.firestore and call firebase_admin.firestore.client();
ensure only one approach is used project-wide (match sync_stats_repository),
remove the incorrect firebase_admin.firestore import, and adjust any type
hints/usages accordingly.

@SuperMuel
SuperMuel merged commit 734cc4c into main Oct 22, 2025
1 check passed
@SuperMuel
SuperMuel deleted the migrate-http-cloud-functions-to-fastapi branch October 22, 2025 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants