Skip to content

Conversation

@sfoale
Copy link
Collaborator

@sfoale sfoale commented Jun 9, 2025

Summary

• Implement complete FastAPI backend to replace legacy Flask application with modern async architecture
• Add comprehensive test coverage for all FastAPI endpoints with 11 test modules covering new code

Key Changes

New FastAPI Backend (server/)

  • Modern async FastAPI application with auto-generated OpenAPI documentation
  • SQLAlchemy 2.0+ models with PostGIS geospatial support
  • Pydantic schemas for request/response validation
  • Modular route structure organized by domain (pointing, instrument, alerts, etc.)
  • JWT authentication system replacing Flask-based auth

Comprehensive Test Suite (tests/fastapi/)

  • 100% endpoint coverage across all API routes
  • Integration tests with database restore functionality
  • Test data fixtures for realistic testing scenarios

Kubernetes Integration (gwtm-helm/)

  • FastAPI deployment templates and configuration
  • Database initialization scripts for containerized environments
  • Development tooling with Skaffold for full-stack testing

This FastAPI implementation aims to maintain full API compatibility while providing better performance, automatic documentation, and type safety.

@sfoale sfoale requested review from Fingel, cmccully and swyatt7 June 9, 2025 15:16
Copy link
Contributor

@Fingel Fingel left a comment

Choose a reason for hiding this comment

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

I spent some time trying to get this running locally, but was ultimately unsuccessful.

I'm not clear on how the database is managed. There are some manual commands in the file database_postgresql_notes but if that is the de-facto method of getting a database set up, shouldn't it be a .sql file?

There seemed to be some syntax errors in there anyway, ($$to add enum type -> alter type bandpass add value 'other';) so even after copying the commands to a .sql file I was unable to use them to set up the database properly.

Without the database, it seems the tests will not run. Additionally, the tests appear to make HTTP requests to 2 different services. I managed to figure that one of them running on port 8000 was the fastapi service itself. But I don't know what the requests going to port 5000 are supposed to test.

All in all, I think the README needs some help.

Flask-Mail==0.10.0
Flask-SQLAlchemy==2.5.1
Flask-WTF
flask-caching
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing fastapi

@sfoale
Copy link
Collaborator Author

sfoale commented Jun 12, 2025

I spent some time trying to get this running locally, but was ultimately unsuccessful.

I'm not clear on how the database is managed. There are some manual commands in the file database_postgresql_notes but if that is the de-facto method of getting a database set up, shouldn't it be a .sql file?

There seemed to be some syntax errors in there anyway, ($$to add enum type -> alter type bandpass add value 'other';) so even after copying the commands to a .sql file I was unable to use them to set up the database properly.

Without the database, it seems the tests will not run. Additionally, the tests appear to make HTTP requests to 2 different services. I managed to figure that one of them running on port 8000 was the fastapi service itself. But I don't know what the requests going to port 5000 are supposed to test.

All in all, I think the README needs some help.

Sorry about that - I've added a bit of signposting from the main readme to the fastapi readme.

With the project in flux with the intention to retire flask in favour of fastapi / svelte I wasn't concentrating so much on the top level README.

In summary, the most straightforward way to get the whole thing up and running - fastapi, database, even the flask app, is to use skaffold - just as
skaffold dev
should bring everything up. Then the tests can be run with a script (provided) - more info in the server/README.

Hope that is a bit clearer now.

@sfoale sfoale requested a review from Fingel June 12, 2025 15:10
@cmccully cmccully requested a review from Copilot June 16, 2025 19:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR replaces the legacy Flask backend with a new async FastAPI application, adds Pydantic-based settings and JWT authentication, and integrates full Kubernetes/Helm/Skaffold deployment plus comprehensive endpoint tests.

  • Introduce a modern FastAPI service in server/ with SQLAlchemy models, Pydantic schemas, and JWT auth
  • Provide Pydantic settings in server/config.py and Dockerfile for containerization
  • Add Helm charts (gwtm-helm/) and Skaffold config for local and production deployments

Reviewed Changes

Copilot reviewed 147 out of 147 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
server/core/enums/bandpass.py New bandpass enum values
server/core/enums/init.py Import list for core enums (duplicate import present)
server/config.py Pydantic Settings class for environment variables
server/auth/auth.py JWT token creation, decoding, and user validation
server/auth/init.py Auth package init
server/README.md FastAPI backend usage and testing instructions
server/Dockerfile Container build for FastAPI service
gwtm-helm/values.yaml Helm values for FastAPI deployment
gwtm-helm/values-dev.yaml Development overrides for Helm
gwtm-helm/templates/ingress.yaml Ingress paths for /api/v1, docs, health
gwtm-helm/templates/fastapi/service.yaml Kubernetes Service definition for FastAPI
gwtm-helm/templates/fastapi/deployment.yaml Kubernetes Deployment for FastAPI with init container
gwtm-helm/templates/fastapi/configmap.yaml ConfigMap for FastAPI environment variables
gwtm-helm/templates/backend/create-database-tables.yaml Database setup Job
gwtm-helm/start-dev.sh Script to start Skaffold dev environment
gwtm-helm/skaffold.yaml Skaffold configuration for both Flask and FastAPI
gwtm-helm/restore-db Script for restoring database dumps
gwtm-helm/fastapi-README.md Documentation for FastAPI Helm templates
README.md Root README with FastAPI quick-start
.dockerignore Updated ignore rules for Docker build
Comments suppressed due to low confidence (2)

server/core/enums/bandpass.py:3

  • Class names should use PascalCase. Rename bandpass to Bandpass to follow Python class naming conventions.
class bandpass(IntEnum):

gwtm-helm/templates/fastapi/configmap.yaml:13

  • CORS_ORIGINS is expected to be a list by the Pydantic settings. Providing a single string may cause parsing errors. Consider formatting it as a JSON array or comma-separated list that your app can split.
CORS_ORIGINS: "{{ .Values.ingress.host }}"

from .pointing_status import pointing_status
from .frequency_units import frequency_units
from .depth_unit import depth_unit
from .frequency_units import frequency_units
Copy link

Copilot AI Jun 16, 2025

Choose a reason for hiding this comment

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

Duplicate import of frequency_units detected. Remove the redundant import to clean up the module.

Suggested change
from .frequency_units import frequency_units

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

server/config.py Outdated
Comment on lines 34 to 35
SECRET_KEY: str = Field(os.urandom(16).hex(), env="SECRET_KEY")
JWT_SECRET_KEY: str = Field(os.urandom(16).hex(), env="JWT_SECRET_KEY")
Copy link

Copilot AI Jun 16, 2025

Choose a reason for hiding this comment

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

Using os.urandom at import time sets the same default each run and may not be re-evaluated per instance. Consider using default_factory=... to generate a new random key for each instantiation.

Suggested change
SECRET_KEY: str = Field(os.urandom(16).hex(), env="SECRET_KEY")
JWT_SECRET_KEY: str = Field(os.urandom(16).hex(), env="JWT_SECRET_KEY")
def _generate_random_key() -> str:
"""Generate a random 16-byte key and return its hexadecimal representation."""
return os.urandom(16).hex()
SECRET_KEY: str = Field(default_factory=_generate_random_key, env="SECRET_KEY")
JWT_SECRET_KEY: str = Field(default_factory=_generate_random_key, env="JWT_SECRET_KEY")

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,17 @@
from enum import IntEnum

class WavelengthUnits(IntEnum):
Copy link
Contributor

Choose a reason for hiding this comment

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

Astropy has a pretty extensive units package developed. Does it help us to use that?

@@ -0,0 +1,35 @@
from enum import IntEnum

class Bandpass(IntEnum):
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels very fragile. Should this be more dynamic and stored in the db?

# CORS settings
CORS_ORIGINS: List[str] = ["*"]
CORS_METHODS: List[str] = ["*"]
CORS_HEADERS: List[str] = ["*"]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should these be set to something that isn't * for prod?

class DepthUnit(IntEnum):
"""Enumeration for depth units."""

ab_mag = 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this also be in the db?

db = SessionLocal()
try:
yield db
finally:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a little unclear on when this finally happens. I think this may prematurely close the db connection. Should this be more of a context manager?

@@ -0,0 +1,10 @@
from .users import Users, UserGroups, Groups, UserActions
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we import these things here in the init if we don't expose them?

from server.core.enums.depthunit import DepthUnit as depth_unit_enum
from geoalchemy2 import Geography
from sqlalchemy.ext.hybrid import hybrid_property
from ..database import Base
Copy link
Contributor

Choose a reason for hiding this comment

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

Importing this effectively global variable makes me nervous. I might advocate for having a get session function and leaving the separation of responsibilities to not touch that variable.

self.warnings = []


class GWCandidate(Base):
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. I'm seeing what you are doing now. I guess this is fine.

Returns:
Tuple of (success, creators_list)
"""
from sqlalchemy import and_
Copy link
Contributor

Choose a reason for hiding this comment

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

Imports in methods make me nervous.

@@ -0,0 +1,11 @@
"""Test endpoint for refactored pointing routes."""
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this file need to persist?

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.

4 participants