Fix SQLAlchemy 2.x compatibility: wrap raw SQL in text()
Some checks failed
Deploy to Coolify / deploy (push) Has been cancelled
Some checks failed
Deploy to Coolify / deploy (push) Has been cancelled
SQLAlchemy 2.x requires raw SQL strings to be explicitly wrapped in text(). Fixed 16 instances across 5 route files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Header
|
from fastapi import APIRouter, HTTPException, Header
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from database import SessionLocal
|
from database import SessionLocal
|
||||||
|
|
||||||
@@ -224,9 +225,9 @@ def log_audit(db, tenant_id: str, action: str, changed_fields: Optional[dict], c
|
|||||||
"""Write an audit log entry."""
|
"""Write an audit log entry."""
|
||||||
try:
|
try:
|
||||||
db.execute(
|
db.execute(
|
||||||
"""INSERT INTO compliance_company_profile_audit
|
text("""INSERT INTO compliance_company_profile_audit
|
||||||
(tenant_id, action, changed_fields, changed_by)
|
(tenant_id, action, changed_fields, changed_by)
|
||||||
VALUES (:tenant_id, :action, :fields::jsonb, :changed_by)""",
|
VALUES (:tenant_id, :action, :fields::jsonb, :changed_by)"""),
|
||||||
{
|
{
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"action": action,
|
"action": action,
|
||||||
@@ -252,7 +253,7 @@ async def get_company_profile(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tenant_id",
|
text(f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tenant_id"),
|
||||||
{"tenant_id": tid},
|
{"tenant_id": tid},
|
||||||
)
|
)
|
||||||
row = result.fetchone()
|
row = result.fetchone()
|
||||||
@@ -276,7 +277,7 @@ async def upsert_company_profile(
|
|||||||
try:
|
try:
|
||||||
# Check if profile exists
|
# Check if profile exists
|
||||||
existing = db.execute(
|
existing = db.execute(
|
||||||
"SELECT id FROM compliance_company_profiles WHERE tenant_id = :tid",
|
text("SELECT id FROM compliance_company_profiles WHERE tenant_id = :tid"),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
@@ -285,7 +286,7 @@ async def upsert_company_profile(
|
|||||||
completed_at_clause = ", completed_at = NOW()" if profile.is_complete else ", completed_at = NULL"
|
completed_at_clause = ", completed_at = NOW()" if profile.is_complete else ", completed_at = NULL"
|
||||||
|
|
||||||
db.execute(
|
db.execute(
|
||||||
f"""INSERT INTO compliance_company_profiles
|
text(f"""INSERT INTO compliance_company_profiles
|
||||||
(tenant_id, company_name, legal_form, industry, founded_year,
|
(tenant_id, company_name, legal_form, industry, founded_year,
|
||||||
business_model, offerings, company_size, employee_count, annual_revenue,
|
business_model, offerings, company_size, employee_count, annual_revenue,
|
||||||
headquarters_country, headquarters_city, has_international_locations,
|
headquarters_country, headquarters_city, has_international_locations,
|
||||||
@@ -344,7 +345,7 @@ async def upsert_company_profile(
|
|||||||
supervisory_authority = EXCLUDED.supervisory_authority,
|
supervisory_authority = EXCLUDED.supervisory_authority,
|
||||||
review_cycle_months = EXCLUDED.review_cycle_months,
|
review_cycle_months = EXCLUDED.review_cycle_months,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
{completed_at_clause}""",
|
{completed_at_clause}"""),
|
||||||
{
|
{
|
||||||
"tid": tid,
|
"tid": tid,
|
||||||
"company_name": profile.company_name,
|
"company_name": profile.company_name,
|
||||||
@@ -392,7 +393,7 @@ async def upsert_company_profile(
|
|||||||
|
|
||||||
# Fetch and return
|
# Fetch and return
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tid",
|
text(f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tid"),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
)
|
)
|
||||||
row = result.fetchone()
|
row = result.fetchone()
|
||||||
@@ -415,7 +416,7 @@ async def delete_company_profile(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
existing = db.execute(
|
existing = db.execute(
|
||||||
"SELECT id FROM compliance_company_profiles WHERE tenant_id = :tid",
|
text("SELECT id FROM compliance_company_profiles WHERE tenant_id = :tid"),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
@@ -423,7 +424,7 @@ async def delete_company_profile(
|
|||||||
raise HTTPException(status_code=404, detail="Company profile not found")
|
raise HTTPException(status_code=404, detail="Company profile not found")
|
||||||
|
|
||||||
db.execute(
|
db.execute(
|
||||||
"DELETE FROM compliance_company_profiles WHERE tenant_id = :tid",
|
text("DELETE FROM compliance_company_profiles WHERE tenant_id = :tid"),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -451,7 +452,7 @@ async def get_template_context(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tid",
|
text(f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tid"),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
)
|
)
|
||||||
row = result.fetchone()
|
row = result.fetchone()
|
||||||
@@ -513,11 +514,11 @@ async def get_audit_log(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
"""SELECT id, action, changed_fields, changed_by, created_at
|
text("""SELECT id, action, changed_fields, changed_by, created_at
|
||||||
FROM compliance_company_profile_audit
|
FROM compliance_company_profile_audit
|
||||||
WHERE tenant_id = :tid
|
WHERE tenant_id = :tid
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT 100""",
|
LIMIT 100"""),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
)
|
)
|
||||||
rows = result.fetchall()
|
rows = result.fetchall()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from typing import Any, Optional
|
|||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Header
|
from fastapi import APIRouter, HTTPException, Header
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from database import SessionLocal
|
from database import SessionLocal
|
||||||
|
|
||||||
@@ -75,13 +76,13 @@ async def get_compliance_scope(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
row = db.execute(
|
row = db.execute(
|
||||||
"""SELECT tenant_id,
|
text("""SELECT tenant_id,
|
||||||
state->'compliance_scope' AS scope,
|
state->'compliance_scope' AS scope,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM sdk_states
|
FROM sdk_states
|
||||||
WHERE tenant_id = :tid
|
WHERE tenant_id = :tid
|
||||||
AND state ? 'compliance_scope'""",
|
AND state ? 'compliance_scope'"""),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
@@ -106,22 +107,22 @@ async def upsert_compliance_scope(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
db.execute(
|
db.execute(
|
||||||
"""INSERT INTO sdk_states (tenant_id, state)
|
text("""INSERT INTO sdk_states (tenant_id, state)
|
||||||
VALUES (:tid, jsonb_build_object('compliance_scope', :scope::jsonb))
|
VALUES (:tid, jsonb_build_object('compliance_scope', :scope::jsonb))
|
||||||
ON CONFLICT (tenant_id) DO UPDATE
|
ON CONFLICT (tenant_id) DO UPDATE
|
||||||
SET state = sdk_states.state || jsonb_build_object('compliance_scope', :scope::jsonb),
|
SET state = sdk_states.state || jsonb_build_object('compliance_scope', :scope::jsonb),
|
||||||
updated_at = NOW()""",
|
updated_at = NOW()"""),
|
||||||
{"tid": tid, "scope": scope_json},
|
{"tid": tid, "scope": scope_json},
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
row = db.execute(
|
row = db.execute(
|
||||||
"""SELECT tenant_id,
|
text("""SELECT tenant_id,
|
||||||
state->'compliance_scope' AS scope,
|
state->'compliance_scope' AS scope,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM sdk_states
|
FROM sdk_states
|
||||||
WHERE tenant_id = :tid""",
|
WHERE tenant_id = :tid"""),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def _get_template_context(db, tid: str) -> dict:
|
|||||||
cp_db = SessionLocal()
|
cp_db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = cp_db.execute(
|
result = cp_db.execute(
|
||||||
f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tid",
|
text(f"SELECT {_BASE_COLUMNS} FROM compliance_company_profiles WHERE tenant_id = :tid"),
|
||||||
{"tid": tid},
|
{"tid": tid},
|
||||||
)
|
)
|
||||||
row = result.fetchone()
|
row = result.fetchone()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from typing import Optional
|
|||||||
import httpx
|
import httpx
|
||||||
from fastapi import APIRouter, File, Form, Header, UploadFile, HTTPException
|
from fastapi import APIRouter, File, Form, Header, UploadFile, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from database import SessionLocal
|
from database import SessionLocal
|
||||||
|
|
||||||
@@ -291,11 +292,11 @@ async def analyze_document(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
db.execute(
|
db.execute(
|
||||||
"""INSERT INTO compliance_imported_documents
|
text("""INSERT INTO compliance_imported_documents
|
||||||
(id, tenant_id, filename, file_type, file_size, detected_type, detection_confidence,
|
(id, tenant_id, filename, file_type, file_size, detected_type, detection_confidence,
|
||||||
extracted_text, extracted_entities, recommendations, status, analyzed_at)
|
extracted_text, extracted_entities, recommendations, status, analyzed_at)
|
||||||
VALUES (:id, :tenant_id, :filename, :file_type, :file_size, :detected_type, :confidence,
|
VALUES (:id, :tenant_id, :filename, :file_type, :file_size, :detected_type, :confidence,
|
||||||
:text, :entities::jsonb, :recommendations::jsonb, 'analyzed', NOW())""",
|
:text, :entities::jsonb, :recommendations::jsonb, 'analyzed', NOW())"""),
|
||||||
{
|
{
|
||||||
"id": doc_id,
|
"id": doc_id,
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
@@ -313,9 +314,9 @@ async def analyze_document(
|
|||||||
if total_gaps > 0:
|
if total_gaps > 0:
|
||||||
import json
|
import json
|
||||||
db.execute(
|
db.execute(
|
||||||
"""INSERT INTO compliance_gap_analyses
|
text("""INSERT INTO compliance_gap_analyses
|
||||||
(tenant_id, document_id, total_gaps, critical_gaps, high_gaps, medium_gaps, low_gaps, gaps, recommended_packages)
|
(tenant_id, document_id, total_gaps, critical_gaps, high_gaps, medium_gaps, low_gaps, gaps, recommended_packages)
|
||||||
VALUES (:tenant_id, :document_id, :total, :critical, :high, :medium, :low, :gaps::jsonb, :packages::jsonb)""",
|
VALUES (:tenant_id, :document_id, :total, :critical, :high, :medium, :low, :gaps::jsonb, :packages::jsonb)"""),
|
||||||
{
|
{
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"document_id": doc_id,
|
"document_id": doc_id,
|
||||||
@@ -358,7 +359,7 @@ async def get_gap_analysis(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
"SELECT * FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid",
|
text("SELECT * FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid"),
|
||||||
{"doc_id": document_id, "tid": tid},
|
{"doc_id": document_id, "tid": tid},
|
||||||
).fetchone()
|
).fetchone()
|
||||||
if not result:
|
if not result:
|
||||||
@@ -374,11 +375,11 @@ async def list_documents(tenant_id: str = "default"):
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
"""SELECT id, filename, file_type, file_size, detected_type, detection_confidence,
|
text("""SELECT id, filename, file_type, file_size, detected_type, detection_confidence,
|
||||||
extracted_entities, recommendations, status, analyzed_at, created_at
|
extracted_entities, recommendations, status, analyzed_at, created_at
|
||||||
FROM compliance_imported_documents
|
FROM compliance_imported_documents
|
||||||
WHERE tenant_id = :tenant_id
|
WHERE tenant_id = :tenant_id
|
||||||
ORDER BY created_at DESC""",
|
ORDER BY created_at DESC"""),
|
||||||
{"tenant_id": tenant_id},
|
{"tenant_id": tenant_id},
|
||||||
)
|
)
|
||||||
rows = result.fetchall()
|
rows = result.fetchall()
|
||||||
@@ -424,11 +425,11 @@ async def delete_document(
|
|||||||
try:
|
try:
|
||||||
# Delete gap analysis first (FK dependency)
|
# Delete gap analysis first (FK dependency)
|
||||||
db.execute(
|
db.execute(
|
||||||
"DELETE FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid",
|
text("DELETE FROM compliance_gap_analyses WHERE document_id = :doc_id AND tenant_id = :tid"),
|
||||||
{"doc_id": document_id, "tid": tid},
|
{"doc_id": document_id, "tid": tid},
|
||||||
)
|
)
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
"DELETE FROM compliance_imported_documents WHERE id = :doc_id AND tenant_id = :tid",
|
text("DELETE FROM compliance_imported_documents WHERE id = :doc_id AND tenant_id = :tid"),
|
||||||
{"doc_id": document_id, "tid": tid},
|
{"doc_id": document_id, "tid": tid},
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from typing import Optional
|
|||||||
import httpx
|
import httpx
|
||||||
from fastapi import APIRouter, File, Form, UploadFile, HTTPException
|
from fastapi import APIRouter, File, Form, UploadFile, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from database import SessionLocal
|
from database import SessionLocal
|
||||||
|
|
||||||
@@ -366,13 +367,13 @@ async def scan_dependencies(
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
db.execute(
|
db.execute(
|
||||||
"""INSERT INTO compliance_screenings
|
text("""INSERT INTO compliance_screenings
|
||||||
(id, tenant_id, status, sbom_format, sbom_version,
|
(id, tenant_id, status, sbom_format, sbom_version,
|
||||||
total_components, total_issues, critical_issues, high_issues, medium_issues, low_issues,
|
total_components, total_issues, critical_issues, high_issues, medium_issues, low_issues,
|
||||||
sbom_data, started_at, completed_at)
|
sbom_data, started_at, completed_at)
|
||||||
VALUES (:id, :tenant_id, 'completed', 'CycloneDX', '1.5',
|
VALUES (:id, :tenant_id, 'completed', 'CycloneDX', '1.5',
|
||||||
:total_components, :total_issues, :critical, :high, :medium, :low,
|
:total_components, :total_issues, :critical, :high, :medium, :low,
|
||||||
:sbom_data::jsonb, :started_at, :completed_at)""",
|
:sbom_data::jsonb, :started_at, :completed_at)"""),
|
||||||
{
|
{
|
||||||
"id": screening_id,
|
"id": screening_id,
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
@@ -391,11 +392,11 @@ async def scan_dependencies(
|
|||||||
# Persist security issues
|
# Persist security issues
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
db.execute(
|
db.execute(
|
||||||
"""INSERT INTO compliance_security_issues
|
text("""INSERT INTO compliance_security_issues
|
||||||
(id, screening_id, severity, title, description, cve, cvss,
|
(id, screening_id, severity, title, description, cve, cvss,
|
||||||
affected_component, affected_version, fixed_in, remediation, status)
|
affected_component, affected_version, fixed_in, remediation, status)
|
||||||
VALUES (:id, :screening_id, :severity, :title, :description, :cve, :cvss,
|
VALUES (:id, :screening_id, :severity, :title, :description, :cve, :cvss,
|
||||||
:component, :version, :fixed_in, :remediation, :status)""",
|
:component, :version, :fixed_in, :remediation, :status)"""),
|
||||||
{
|
{
|
||||||
"id": issue["id"],
|
"id": issue["id"],
|
||||||
"screening_id": screening_id,
|
"screening_id": screening_id,
|
||||||
@@ -486,10 +487,10 @@ async def get_screening(screening_id: str):
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
"""SELECT id, status, sbom_format, sbom_version,
|
text("""SELECT id, status, sbom_format, sbom_version,
|
||||||
total_components, total_issues, critical_issues, high_issues,
|
total_components, total_issues, critical_issues, high_issues,
|
||||||
medium_issues, low_issues, sbom_data, started_at, completed_at
|
medium_issues, low_issues, sbom_data, started_at, completed_at
|
||||||
FROM compliance_screenings WHERE id = :id""",
|
FROM compliance_screenings WHERE id = :id"""),
|
||||||
{"id": screening_id},
|
{"id": screening_id},
|
||||||
)
|
)
|
||||||
row = result.fetchone()
|
row = result.fetchone()
|
||||||
@@ -498,9 +499,9 @@ async def get_screening(screening_id: str):
|
|||||||
|
|
||||||
# Fetch issues
|
# Fetch issues
|
||||||
issues_result = db.execute(
|
issues_result = db.execute(
|
||||||
"""SELECT id, severity, title, description, cve, cvss,
|
text("""SELECT id, severity, title, description, cve, cvss,
|
||||||
affected_component, affected_version, fixed_in, remediation, status
|
affected_component, affected_version, fixed_in, remediation, status
|
||||||
FROM compliance_security_issues WHERE screening_id = :id""",
|
FROM compliance_security_issues WHERE screening_id = :id"""),
|
||||||
{"id": screening_id},
|
{"id": screening_id},
|
||||||
)
|
)
|
||||||
issues_rows = issues_result.fetchall()
|
issues_rows = issues_result.fetchall()
|
||||||
@@ -566,12 +567,12 @@ async def list_screenings(tenant_id: str = "default"):
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
result = db.execute(
|
result = db.execute(
|
||||||
"""SELECT id, status, total_components, total_issues,
|
text("""SELECT id, status, total_components, total_issues,
|
||||||
critical_issues, high_issues, medium_issues, low_issues,
|
critical_issues, high_issues, medium_issues, low_issues,
|
||||||
started_at, completed_at, created_at
|
started_at, completed_at, created_at
|
||||||
FROM compliance_screenings
|
FROM compliance_screenings
|
||||||
WHERE tenant_id = :tenant_id
|
WHERE tenant_id = :tenant_id
|
||||||
ORDER BY created_at DESC""",
|
ORDER BY created_at DESC"""),
|
||||||
{"tenant_id": tenant_id},
|
{"tenant_id": tenant_id},
|
||||||
)
|
)
|
||||||
rows = result.fetchall()
|
rows = result.fetchall()
|
||||||
|
|||||||
Reference in New Issue
Block a user