Skip to content

Conversation

@ChingEnLin
Copy link
Owner

This pull request introduces a new audit log query and visualization feature to both the backend and frontend, enabling users to ask natural language questions about audit logs and receive summarized results with recommended chart visualizations. The backend now supports an /audit/query endpoint that translates user questions into SQL, executes them safely, and summarizes results. The frontend adds navigation for audit logs, unified authentication handling, and a new chart component for displaying visualizations.

Backend: Audit Log Query & Summarization

  • Added a new audit route (backend/routes/audit.py) with a /audit/query endpoint that accepts a natural language question, validates auth, and returns SQL, results, summary, and visualization config.
  • Implemented the audit service (backend/services/audit_service.py) to orchestrate: NL-to-SQL conversion, safe SQL execution (SELECT-only), and result summarization with visualization config using Gemini.
  • Extended Gemini service (backend/services/gemini_service.py) with prompt templates and logic for generating audit SQL and summarizing results, including a structured response for chart visualization. [1] [2]
  • Registered the audit router in the FastAPI app (backend/main.py). [1] [2]
  • Added unit tests for audit service logic (backend/tests/test_audit_service.py).

Frontend: Audit Log UI & Visualization

  • Added a new ChartDisplay component (frontend/components/ChartDisplay.tsx) to render bar, line, and pie charts based on backend-provided visualization config.
  • Updated navigation bar (frontend/components/NavBar.tsx) to include an "Audit Log" section and theme/user controls.
  • Implemented a unified authentication hook (frontend/hooks/useUnifiedAuth.ts) to support both MSAL and context-based auth flows.
  • Updated dependencies to include recharts and react-markdown for charting and markdown rendering. (frontend/package.json)

…arkdown support

- Added AuditPage component with form for user queries
- Integrated ReactMarkdown for displaying AI insights
- Implemented data fetching from the API with error handling
- Created AuditPage.module.css for styling the new page
- Updated router to include protected route for AuditPage
- Added new dependencies: react-markdown and recharts
Copilot AI review requested due to automatic review settings January 12, 2026 10:13
Copy link
Contributor

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 pull request adds a comprehensive audit log analysis feature that enables users to query audit logs using natural language, with AI-powered summarization and automatic visualization recommendations. The implementation spans both backend and frontend, introducing new endpoints, services, UI components, and authentication utilities.

Changes:

  • Added backend /audit/query endpoint with NL-to-SQL translation, safe query execution, and AI summarization with visualization configs
  • Implemented frontend AuditPage with chart visualization support using Recharts library
  • Created unified authentication hook to support both MSAL and context-based auth flows

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
backend/routes/audit.py New audit endpoint for processing natural language queries
backend/services/audit_service.py Orchestrates SQL generation, execution, and result summarization
backend/services/gemini_service.py Extended with audit SQL generation and result summarization using Gemini
backend/main.py Registered audit router
backend/tests/test_audit_service.py Unit tests for audit service
frontend/pages/AuditPage.tsx New page for audit log querying with visualization
frontend/components/ChartDisplay.tsx Recharts-based component for rendering bar, line, and pie charts
frontend/components/NavBar.tsx Navigation bar with audit log link and theme controls
frontend/hooks/useUnifiedAuth.ts Unified auth hook supporting MSAL and bypass modes
frontend/router.tsx Added /audit route with protection
frontend/package.json Added react-markdown and recharts dependencies
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +40 to +44
const chartData = data.map(item => ({
...item,
// Create a display label for X axis if x_key exists
...(config.x_key && { [config.x_key]: item[config.x_key] })
}));
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The visualization data processing in ChartDisplay doesn't validate that the required keys (x_key, y_key) actually exist in the data. If the backend provides a visualization config with keys that don't match the actual data structure, this will cause rendering errors. Add validation to check that all referenced keys exist in the data before rendering.

Copilot uses AI. Check for mistakes.
cx="50%"
cy="50%"
labelLine={false}
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The pie chart label renders percentages but doesn't handle edge cases where the percentage calculation might fail or produce NaN. When data values are zero or invalid, the label expression (percent * 100).toFixed(0) could produce unexpected results. Add defensive checks for valid numeric values before rendering.

Suggested change
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
label={({ name, percent }) => {
const isValidPercent = typeof percent === 'number' && Number.isFinite(percent);
const percentValue = isValidPercent ? (percent * 100).toFixed(0) : '0';
return `${name} ${percentValue}%`;
}}

Copilot uses AI. Check for mistakes.
Comment on lines 49 to 55
# If the generator returned an error query or invalid SQL, return it
if "Error:" in sql_query:
return {
"sql_query": sql_query,
"results": [],
"summary": "Could not generate a valid query for your request."
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The process_audit_question function checks for errors using string matching ("Error:" in sql_query) which is fragile and could produce false positives if legitimate queries contain the word "Error:". Use a more robust error handling mechanism such as custom exception types or a Result wrapper type.

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +118
<table className={styles.table}>
<thead>
<tr>
{Object.keys(data.results[0]).map((key) => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{data.results.map((row, i) => (
<tr key={i}>
{Object.values(row).map((val: any, j) => (
<td key={j}>
{typeof val === 'object' ? JSON.stringify(val) : String(val)}
</td>
))}
</tr>
))}
</tbody>
</table>
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The table rendering assumes all rows have the same keys as the first row (line 102). If the SQL results have inconsistent structure across rows (e.g., some rows with additional fields), this will cause incorrect rendering. Consider collecting all unique keys across all rows or handling missing keys gracefully.

Suggested change
<table className={styles.table}>
<thead>
<tr>
{Object.keys(data.results[0]).map((key) => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{data.results.map((row, i) => (
<tr key={i}>
{Object.values(row).map((val: any, j) => (
<td key={j}>
{typeof val === 'object' ? JSON.stringify(val) : String(val)}
</td>
))}
</tr>
))}
</tbody>
</table>
{(() => {
const allKeys = Array.from(
new Set(
data.results.flatMap((row) =>
row && typeof row === 'object'
? Object.keys(row)
: []
)
)
);
return (
<table className={styles.table}>
<thead>
<tr>
{allKeys.map((key) => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{data.results.map((row, i) => (
<tr key={i}>
{allKeys.map((key, j) => {
const val =
row && typeof row === 'object'
? (row as any)[key]
: undefined;
const display =
val === undefined || val === null
? ''
: typeof val === 'object'
? JSON.stringify(val)
: String(val);
return <td key={j}>{display}</td>;
})}
</tr>
))}
</tbody>
</table>
);
})()}

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 34
conn = get_connection()
cur = conn.cursor()
cur.execute(sql_query)

# Get column names
columns = [desc[0] for desc in cur.description]
results = [dict(zip(columns, row)) for row in cur.fetchall()]

# Serialize datetime and json objects
for row in results:
for key, value in row.items():
if hasattr(value, 'isoformat'):
row[key] = value.isoformat()
elif isinstance(value, dict):
# Ensure dicts (like diff_data) are kept as dicts for the frontend
pass

cur.close()
conn.close()
return results
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The audit service closes database connections after each query execution. This is correct for preventing connection leaks, but the code should use a context manager pattern or try-finally block to ensure connections are properly closed even if an exception occurs before reaching the close() calls.

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 30

# We might want to validate the token here even if we don't use it for the pg connection directly yet
# user_token = authorization.replace("Bearer ", "")
# access_token = exchange_token_obo(user_token)

Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The audit route extracts the user token but doesn't validate it or use it for authorization. The commented code suggests token validation was intended but not implemented. This leaves the endpoint vulnerable to unauthorized access. Either implement proper token validation using exchange_token_obo or add a clear TODO comment explaining why validation is deferred.

Suggested change
# We might want to validate the token here even if we don't use it for the pg connection directly yet
# user_token = authorization.replace("Bearer ", "")
# access_token = exchange_token_obo(user_token)
# Validate the user token using OBO flow; this ensures only authorized callers can access this route
user_token = authorization.replace("Bearer ", "").strip()
try:
# We currently don't use the exchanged access token further in this handler,
# but calling this function validates the incoming user token.
exchange_token_obo(user_token)
except Exception as exc:
raise HTTPException(status_code=401, detail="Invalid or expired token") from exc

Copilot uses AI. Check for mistakes.
from pydantic import BaseModel
from typing import List, Dict, Any, Optional
from services.audit_service import process_audit_question
from services.azure_auth import exchange_token_obo
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Import of 'exchange_token_obo' is not used.

Suggested change
from services.azure_auth import exchange_token_obo

Copilot uses AI. Check for mistakes.
from typing import List, Dict, Any, Optional
from services.audit_service import process_audit_question
from services.azure_auth import exchange_token_obo
from services.gemini_service import VisualizationConfig, AuditSummaryResponse
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Import of 'AuditSummaryResponse' is not used.

Suggested change
from services.gemini_service import VisualizationConfig, AuditSummaryResponse
from services.gemini_service import VisualizationConfig

Copilot uses AI. Check for mistakes.
from typing import Dict, Any
from services.pg_connection import get_connection
from services.gemini_service import generate_audit_sql, summarize_audit_results
import json
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Import of 'json' is not used.

Suggested change
import json

Copilot uses AI. Check for mistakes.
from google.genai import types
from models.schemas import GeneratedCode, CollectionContext, DebugSuggestionResponse
from pydantic import BaseModel, Field
from typing import Optional, List, Any, Dict
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Import of 'Any' is not used.
Import of 'Dict' is not used.

Suggested change
from typing import Optional, List, Any, Dict
from typing import Optional, List

Copilot uses AI. Check for mistakes.
@ChingEnLin ChingEnLin merged commit 254e838 into dev Jan 12, 2026
3 checks passed
@ChingEnLin ChingEnLin deleted the feat/data_analysis branch January 12, 2026 10:59
@github-actions
Copy link

🎉 This PR is included in version 2.5.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants