Skip to content

Remove bootstrap frontend and keep SPA routes only#38

Merged
jruszo merged 2 commits intomasterfrom
fix/remove-bootstrap-frontend
Apr 29, 2026
Merged

Remove bootstrap frontend and keep SPA routes only#38
jruszo merged 2 commits intomasterfrom
fix/remove-bootstrap-frontend

Conversation

@jruszo
Copy link
Copy Markdown
Owner

@jruszo jruszo commented Apr 28, 2026

Summary

  • Remove the legacy Django-rendered bootstrap frontend, including views, templates, and the old root URL wiring
  • Repoint remaining workflow, archive, and permission redirects to SPA routes
  • Trim tests and shared error handling to match the API-first setup

Testing

  • Updated backend unit tests for the affected redirects and context helpers
  • Ran the targeted Django test set for common, sql, and auth-related modules
  • Verified formatting with Black

Summary by CodeRabbit

  • New Features

    • Migration toward a single-page application (SPA) experience with SPA-style workflow links and navigation.
  • Changes

    • Removed many server-rendered pages (dashboard, workflows, SQL editor/submit, archives, instance/group management, permission UIs).
    • Simplified authentication flow and removed legacy login/2FA pages and middleware behavior.
    • Error pages converted to self-contained HTML documents.
    • Admin UI routes and related pages removed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR removes the server-side Django rendering layer for many features: deletes numerous view modules, templates, authentication middleware and handlers, prunes URL routing, and replaces several server redirects with SPA route helpers and SPA-focused URL generation.

Changes

Cohort / File(s) Summary
Core View & Auth Removal
sql/views.py, common/auth.py
Deletes primary Django view handlers for workflows, queries, auth flows (login/sign_up/sign_out) and removes the ArcheryAuth class.
Templates Removed (Common & App)
common/templates/..., sql/templates/...
Deletes base layout, login, 2FA, dashboard, and ~30+ SQL-related templates (workflow, query, export, archive, instance, group, rollback, etc.).
Middleware & Settings
common/middleware/check_login_middleware.py, archery/settings.py
Removes CheckLoginMiddleware from code and settings; also removes django.contrib.admin from INSTALLED_APPS.
URL Routing & Entrypoints
sql/urls.py, archery/urls.py
Removes sql app URL patterns and admin route; retains only API namespace in root URLs.
Dashboard Module
common/dashboard.py
Deletes dashboard endpoints, chart-building logic, and date validation helpers.
SPA Utilities & Redirects
common/utils/spa.py, sql/notify.py, sql/archiver.py, sql/query_privileges.py, sql/sql_workflow.py
Adds SPA URL helpers (spa_path_for_workflow, spa_url_for_workflow) and updates redirect/notification code to produce SPA routes instead of Django reverse() targets.
Admin Config Removal
sql/admin.py
Removes model admin registrations and admin classes for SQL app models.
Tests Pruned / Refactored
sql/tests.py, sql/test_archiver.py, sql/test_query_privileges.py, sql/test_workflow.py, common/tests.py, sql/utils/test_workflow_audit.py, sql/test_openai.py, api_instances/tests.py, api_core/tests.py
Deletes many server-rendered view/tests and refactors remaining tests to align with SPA redirects and direct-view invocation (RequestFactory). Some tests removed or simplified.
Miscellaneous Removed Views
common/check.py, common/dashboard.py (already listed)
Removes superuser-only validation endpoints (go_inception/email/instance/file_storage_connect) and supporting handlers.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant Server as Django Backend
    participant SPA as Frontend SPA
    participant API as Backend API

    Browser->>Server: GET /sql/detail/<id> (old server-rendered route)
    alt route removed
        Server-->>Browser: 302 -> SPA path (spa_path_for_workflow -> /workflows/<id>)
        Browser->>SPA: navigate /workflows/<id>
        SPA->>API: GET /api/workflow/<id> (load data via API)
        API-->>SPA: 200 + workflow JSON
        SPA-->>Browser: render workflow detail
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🐰 I hopped through templates, fields, and views,

Left only APIs and SPA-bright hues.
No base, no login modal in sight—
Just /workflows/ guiding the night.
Tiny paws, a lighter backend—what a flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Remove bootstrap frontend and keep SPA routes only' clearly summarizes the main change: removing the legacy Bootstrap-rendered frontend while retaining SPA routes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/remove-bootstrap-frontend

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

Comment thread sql/sql_workflow.py Fixed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
sql/urls.py (1)

24-99: ⚠️ Potential issue | 🔴 Critical

Verify SPA deep links are still served after this change.

sql/sql_workflow.py:39 and sql/archiver.py:875 redirect users to /workflows/{id} and /archives/{id}, but these paths don't match any route in the current URLconf. The root archery/urls.py has no catch-all route or SPA entrypoint handler, so these redirects will result in 404s instead of the SPA shell loading with the deep link.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sql/urls.py` around lines 24 - 99, Redirects in sql_workflow.py (around the
redirect on line ~39) and archiver.py (around the redirect on line ~875) send
users to /workflows/{id} and /archives/{id} which are not defined in the
URLconf; add routes to this urls.py to serve the SPA entrypoint for those deep
links (e.g., add path("workflows/<int:id>/", <SPA_entry_view>) and
path("archives/<int:id>/", <SPA_entry_view>)) so the SPA shell loads for deep
links, or alternatively change the redirects in sql_workflow.redirect and
archiver.archive_apply/archive_switch (the specific redirect calls) to point to
existing valid routes; ensure the chosen SPA entry view is the same handler used
by your frontend root so deep links render correctly.
🧹 Nitpick comments (3)
sql/notify.py (1)

41-48: Prefer fail-fast behavior for unsupported workflow types.

Returning base_url on unknown types can silently produce incorrect links. Raising a clear error (or logging + explicit default path) will make routing drift easier to catch.

Proposed refactor
 def _spa_workflow_url(base_url, workflow_type, workflow_id):
     if workflow_type == WorkflowType.SQL_REVIEW:
         return f"{base_url}/workflows/{workflow_id}"
     if workflow_type == WorkflowType.QUERY:
         return f"{base_url}/permission-management?requestId={workflow_id}"
     if workflow_type == WorkflowType.ARCHIVE:
         return f"{base_url}/archives/{workflow_id}"
-    return base_url
+    raise ValueError(f"Unsupported workflow type for SPA URL: {workflow_type}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sql/notify.py` around lines 41 - 48, The helper _spa_workflow_url currently
returns base_url for unknown WorkflowType values which hides routing errors;
change it to fail-fast by raising a clear exception (e.g., ValueError) when
workflow_type is not one of WorkflowType.SQL_REVIEW, WorkflowType.QUERY, or
WorkflowType.ARCHIVE, or alternatively log an explicit error and return a
well-documented default path; update any callers of _spa_workflow_url to handle
the exception if needed, and reference WorkflowType and _spa_workflow_url when
making the change so the behavior is consistent across the codebase.
common/middleware/check_login_middleware.py (1)

5-14: Consider removing this middleware from the stack now that it is a no-op.

With unconditional return None, this class adds per-request overhead and can imply login enforcement that no longer exists. Prefer removing common.middleware.check_login_middleware.CheckLoginMiddleware from MIDDLEWARE (or renaming/repurposing it to match current behavior).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/middleware/check_login_middleware.py` around lines 5 - 14, The
CheckLoginMiddleware class (subclassing MiddlewareMixin) only defines
process_request and unconditionally returns None, making it a no-op that adds
overhead and misleading behavior; either remove
common.middleware.check_login_middleware.CheckLoginMiddleware from the Django
MIDDLEWARE setting or update this file: delete the CheckLoginMiddleware class
entirely, or rename/repurpose it and implement real logic in process_request (or
other middleware hooks) if you need retained functionality, and update any
references to the class accordingly so the middleware stack no longer includes a
no-op.
sql/sql_workflow.py (1)

38-39: Centralize SPA redirect construction.

/workflows/{id} is now hardcoded here, while /archives/{id} is hardcoded separately in sql/archiver.py. That makes future SPA route or base-path changes easy to miss. A shared redirect helper/config value would keep these post-action redirects from drifting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sql/sql_workflow.py` around lines 38 - 39, Centralize SPA redirect
construction by extracting the hardcoded path into a shared helper or config and
update callers: create a single helper (e.g., spa_redirect(resource, id) or
spa_url(resource, id)) and use it instead of hardcoding
"/workflows/{workflow_id}" in _workflow_detail_redirect and the "/archives/{id}"
use in sql/archiver.py; update _workflow_detail_redirect to call the new helper
(preserving its signature) and change the archiver redirect to the same helper
so future base-path or route changes are made in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@sql/urls.py`:
- Around line 24-99: Redirects in sql_workflow.py (around the redirect on line
~39) and archiver.py (around the redirect on line ~875) send users to
/workflows/{id} and /archives/{id} which are not defined in the URLconf; add
routes to this urls.py to serve the SPA entrypoint for those deep links (e.g.,
add path("workflows/<int:id>/", <SPA_entry_view>) and path("archives/<int:id>/",
<SPA_entry_view>)) so the SPA shell loads for deep links, or alternatively
change the redirects in sql_workflow.redirect and
archiver.archive_apply/archive_switch (the specific redirect calls) to point to
existing valid routes; ensure the chosen SPA entry view is the same handler used
by your frontend root so deep links render correctly.

---

Nitpick comments:
In `@common/middleware/check_login_middleware.py`:
- Around line 5-14: The CheckLoginMiddleware class (subclassing MiddlewareMixin)
only defines process_request and unconditionally returns None, making it a no-op
that adds overhead and misleading behavior; either remove
common.middleware.check_login_middleware.CheckLoginMiddleware from the Django
MIDDLEWARE setting or update this file: delete the CheckLoginMiddleware class
entirely, or rename/repurpose it and implement real logic in process_request (or
other middleware hooks) if you need retained functionality, and update any
references to the class accordingly so the middleware stack no longer includes a
no-op.

In `@sql/notify.py`:
- Around line 41-48: The helper _spa_workflow_url currently returns base_url for
unknown WorkflowType values which hides routing errors; change it to fail-fast
by raising a clear exception (e.g., ValueError) when workflow_type is not one of
WorkflowType.SQL_REVIEW, WorkflowType.QUERY, or WorkflowType.ARCHIVE, or
alternatively log an explicit error and return a well-documented default path;
update any callers of _spa_workflow_url to handle the exception if needed, and
reference WorkflowType and _spa_workflow_url when making the change so the
behavior is consistent across the codebase.

In `@sql/sql_workflow.py`:
- Around line 38-39: Centralize SPA redirect construction by extracting the
hardcoded path into a shared helper or config and update callers: create a
single helper (e.g., spa_redirect(resource, id) or spa_url(resource, id)) and
use it instead of hardcoding "/workflows/{workflow_id}" in
_workflow_detail_redirect and the "/archives/{id}" use in sql/archiver.py;
update _workflow_detail_redirect to call the new helper (preserving its
signature) and change the archiver redirect to the same helper so future
base-path or route changes are made in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c75d8e31-99a7-4fd6-bfc2-e55cc245091a

📥 Commits

Reviewing files that changed from the base of the PR and between eea837c and 877c138.

📒 Files selected for processing (42)
  • common/dashboard.py
  • common/middleware/check_login_middleware.py
  • common/templates/2fa.html
  • common/templates/base.html
  • common/templates/config.html
  • common/templates/dashboard.html
  • common/templates/error.html
  • common/templates/errors/400.html
  • common/templates/errors/403.html
  • common/templates/errors/404.html
  • common/templates/errors/500.html
  • common/templates/login.html
  • common/tests.py
  • sql/archiver.py
  • sql/notify.py
  • sql/query_privileges.py
  • sql/sql_workflow.py
  • sql/templates/archive.html
  • sql/templates/archivedetail.html
  • sql/templates/detail.html
  • sql/templates/group.html
  • sql/templates/groupmgmt.html
  • sql/templates/instance.html
  • sql/templates/legacy_login_form.html
  • sql/templates/queryapplydetail.html
  • sql/templates/queryapplylist.html
  • sql/templates/queryuserprivileges.html
  • sql/templates/rollback.html
  • sql/templates/sqlexportsubmit.html
  • sql/templates/sqlexportworkflow.html
  • sql/templates/sqlquery.html
  • sql/templates/sqlsubmit.html
  • sql/templates/sqlworkflow.html
  • sql/templates/workflow.html
  • sql/templates/workflow_display.html
  • sql/test_archiver.py
  • sql/test_query_privileges.py
  • sql/test_workflow.py
  • sql/tests.py
  • sql/urls.py
  • sql/utils/test_workflow_audit.py
  • sql/views.py
💤 Files with no reviewable changes (26)
  • sql/utils/test_workflow_audit.py
  • sql/test_workflow.py
  • sql/templates/queryapplydetail.html
  • sql/templates/legacy_login_form.html
  • sql/templates/group.html
  • sql/templates/instance.html
  • sql/templates/groupmgmt.html
  • sql/templates/queryapplylist.html
  • common/templates/login.html
  • sql/templates/sqlexportsubmit.html
  • sql/templates/workflow_display.html
  • sql/templates/queryuserprivileges.html
  • common/templates/base.html
  • common/templates/2fa.html
  • common/dashboard.py
  • sql/templates/sqlworkflow.html
  • sql/templates/sqlquery.html
  • common/templates/dashboard.html
  • sql/templates/detail.html
  • sql/templates/archivedetail.html
  • sql/templates/sqlsubmit.html
  • sql/templates/workflow.html
  • sql/templates/sqlexportworkflow.html
  • sql/views.py
  • sql/templates/rollback.html
  • sql/templates/archive.html

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
sql/test_openai.py (1)

8-15: Close client in fixture teardown, not only in test_init.

openai_client.client.close() is called at Line 21 only for one test. Move cleanup into the fixture so all tests close resources consistently.

♻️ Proposed refactor
 `@pytest.fixture`
 def openai_client(setup_sys_config):
     # Use mock config values.
     setup_sys_config.set("openai_base_url", "https://api.openai.com")
     setup_sys_config.set("openai_api_key", "sk-xxxx")
     setup_sys_config.set("default_chat_model", "gpt-3.5-turbo")
-    yield OpenaiClient()
+    client = OpenaiClient()
+    try:
+        yield client
+    finally:
+        client.client.close()
@@
 def test_init(openai_client):
     assert openai_client.base_url == "https://api.openai.com"
     assert openai_client.api_key == "sk-xxxx"
     assert openai_client.default_chat_model == "gpt-3.5-turbo"
-    openai_client.client.close()

Also applies to: 17-22

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sql/test_openai.py` around lines 8 - 15, The fixture openai_client currently
yields an OpenaiClient() but the client is only closed in one test; move the
cleanup into the fixture by using yield in openai_client and closing the
underlying resource after yield (call openai_client.client.close() or
OpenaiClient.close() as appropriate) so every test gets the instance and the
fixture always performs teardown; locate the openai_client fixture and add the
post-yield cleanup that closes the client's transport/connection using the
OpenaiClient instance created from setup_sys_config.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sql/test_openai.py`:
- Around line 3-5: Test coverage for generate_sql and check_openai was
removed—add function-level tests that exercise their validation and error
branches: write tests that call check_openai to assert errors when the OpenAI
config is missing or invalid and when an OpenaiClient instance is not provided,
and write tests for generate_sql to assert it raises/returns the expected
validation errors when query_desc or db_type are missing and when OpenaiClient
interaction fails; reference the functions check_openai and generate_sql in
sql/query.py and reuse or mock the OpenaiClient behavior (from
common.utils.openai.OpenaiClient) to simulate success and failure paths.

---

Nitpick comments:
In `@sql/test_openai.py`:
- Around line 8-15: The fixture openai_client currently yields an OpenaiClient()
but the client is only closed in one test; move the cleanup into the fixture by
using yield in openai_client and closing the underlying resource after yield
(call openai_client.client.close() or OpenaiClient.close() as appropriate) so
every test gets the instance and the fixture always performs teardown; locate
the openai_client fixture and add the post-yield cleanup that closes the
client's transport/connection using the OpenaiClient instance created from
setup_sys_config.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa4d049b-fae4-4dc7-b20a-a56d268b23aa

📥 Commits

Reviewing files that changed from the base of the PR and between 877c138 and 40271ec.

📒 Files selected for processing (19)
  • api_core/tests.py
  • api_instances/tests.py
  • archery/settings.py
  • archery/urls.py
  • common/auth.py
  • common/check.py
  • common/middleware/check_login_middleware.py
  • common/tests.py
  • common/utils/spa.py
  • sql/admin.py
  • sql/archiver.py
  • sql/notify.py
  • sql/query_privileges.py
  • sql/sql_workflow.py
  • sql/test_archiver.py
  • sql/test_openai.py
  • sql/test_query_privileges.py
  • sql/tests.py
  • sql/urls.py
💤 Files with no reviewable changes (7)
  • api_core/tests.py
  • archery/settings.py
  • sql/urls.py
  • common/check.py
  • common/middleware/check_login_middleware.py
  • archery/urls.py
  • sql/admin.py
✅ Files skipped from review due to trivial changes (1)
  • api_instances/tests.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • sql/query_privileges.py
  • sql/archiver.py
  • common/tests.py
  • sql/test_query_privileges.py

Comment thread sql/test_openai.py
Comment on lines +3 to +5
import pytest

setup_sys_config.set("openai_api_key", "sk-xxxx")
response = admin_client.get("/check/openai/")
assert response.status_code == 200
assert response.json()["data"] == True
from common.utils.openai import OpenaiClient
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Re-add coverage for check_openai / generate_sql behavior.

This test file now validates only OpenaiClient, but sql/query.py still contains generate_sql and check_openai (source snippet: sql/query.py:339-414). Removing their tests drops protection for core validation/error branches (missing config, missing query_desc/db_type, missing instance).

🧪 Suggested test additions (route-independent, function-level)
+import json
+from django.test import RequestFactory
+from sql.query import check_openai, generate_sql
+from common.models.instance import Instance
+
+def test_check_openai_missing_config(setup_sys_config):
+    setup_sys_config.set("openai_base_url", "")
+    setup_sys_config.set("openai_api_key", "")
+    setup_sys_config.set("default_chat_model", "")
+    request = RequestFactory().get("/check/openai/")
+    response = check_openai(request)
+    payload = json.loads(response.content)
+    assert payload["status"] == 1
+    assert payload["data"] is False
+
+def test_generate_sql_missing_required_fields():
+    request = RequestFactory().post("/query/generate_sql/", data={})
+    response = generate_sql(request)
+    payload = json.loads(response.content)
+    assert payload["status"] == 1
+    assert payload["msg"] == "query_desc or db_type does not exist"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sql/test_openai.py` around lines 3 - 5, Test coverage for generate_sql and
check_openai was removed—add function-level tests that exercise their validation
and error branches: write tests that call check_openai to assert errors when the
OpenAI config is missing or invalid and when an OpenaiClient instance is not
provided, and write tests for generate_sql to assert it raises/returns the
expected validation errors when query_desc or db_type are missing and when
OpenaiClient interaction fails; reference the functions check_openai and
generate_sql in sql/query.py and reuse or mock the OpenaiClient behavior (from
common.utils.openai.OpenaiClient) to simulate success and failure paths.

@jruszo jruszo merged commit 9ed6dd5 into master Apr 29, 2026
7 checks passed
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