Skip to content

Commit 3113c63

Browse files
committed
backup commit
1 parent bef22c8 commit 3113c63

117 files changed

Lines changed: 7147 additions & 976 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.sample

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ ANALYTICQ_DB_URL=postgresql+asyncpg://${POSTGRES_NON_ROOT_USER}:${POSTGRES_NON_R
3535
# Secondary storage URL
3636
ANALYTICQ_DB_MONGO_URL=mongodb://${MONGO_INITDB_USERNAME}:${MONGO_INITDB_PASSWORD}@${MONGO_HOST}:${MONGO_PORT}/${MONGO_INITDB_DATABASE}/?authSource=${MONGO_AUTH_SOURCE}
3737

38+
FRONTEND_CORS_HOST=localhost
39+
FRONTEND_CORS_HOST=5173
40+
41+
ANALYTICQ_FRONTEND_CORS_URL=http://${FRONTEND_CORS_HOST}:${FRONTEND_CORS_PORT}
42+
3843
# ============================
3944
# RabbitMQ Configuration
4045
# ============================

analyticq-backend/analyticq/api/contexts.py

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@
44
from analyticq.repository.context_repository import (
55
AnalyticQContextModel, AnalyticQContextRepository,
66
AnalyticQSASTScanResultModel)
7+
from analyticq.repository.stats_repository import AnalyticQStatsRepository
78
from analyticq.schemas.context_dto import PaginatedContextResponse
9+
from analyticq.schemas.error_dto import ErrorResponse
810
from analyticq.service.context_service import AnalyticQContextService
11+
from analyticq.service.stats_service import (AnalyticQStatsModel,
12+
AnalyticQStatsService)
913
from fastapi import APIRouter, Depends, Query, status
10-
from pydantic import BaseModel
1114

1215
logger = logging.getLogger(__name__)
1316

1417
context_router = APIRouter(prefix="/contexts", tags=["Context Management"])
1518

1619

17-
class ErrorResponse(BaseModel):
18-
detail: str
20+
def get_stats_service(
21+
repo: AnalyticQStatsRepository = Depends(AnalyticQStatsRepository)
22+
) -> AnalyticQStatsService:
23+
"""Dependency provider for stats service."""
24+
return AnalyticQStatsService(repo)
1925

2026

2127
def get_context_service(
@@ -68,23 +74,45 @@ async def get_all_contexts(
6874
}
6975

7076

71-
def get_context_service(
72-
repo: AnalyticQContextRepository = Depends(AnalyticQContextRepository)
73-
) -> AnalyticQContextService:
74-
"""Dependency provider for context service."""
75-
return AnalyticQContextService(repo)
77+
@context_router.get(
78+
"/repo/prefix/{prefix}",
79+
response_model=List[AnalyticQContextModel],
80+
summary="Get contexts by repository name prefix",
81+
responses={
82+
status.HTTP_404_NOT_FOUND: {"model": ErrorResponse, "description": "No matching contexts found"},
83+
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse, "description": "Internal server error"}
84+
}
85+
)
86+
async def get_contexts_by_repo_prefix(
87+
prefix: str,
88+
service: AnalyticQContextService = Depends(get_context_service)
89+
):
90+
"""
91+
Get all contexts whose repository name starts with the given prefix.
92+
93+
Args:
94+
prefix (str): The prefix to filter repository names.
95+
service (AnalyticQContextService): The service to handle context operations (injected dependency).
96+
97+
Returns:
98+
list[Context]: A list of contexts matching the repository name prefix.
99+
100+
Raises:
101+
HTTPException: If there's an error retrieving the contexts.
102+
"""
103+
return await service.get_contexts_by_repo_prefix(prefix)
76104

77105

78106
@context_router.get(
79-
"/{repo_name}",
107+
"/repo/{repo_name}",
80108
response_model=AnalyticQContextModel,
81109
summary="Get context by repository name",
82110
responses={
83111
status.HTTP_404_NOT_FOUND: {"model": ErrorResponse, "description": "Context not found"},
84112
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse, "description": "Internal server error"},
85113
},
86114
)
87-
async def get_context(
115+
async def get_context_by_repo_name(
88116
repo_name: str,
89117
service: AnalyticQContextService = Depends(get_context_service)
90118
):
@@ -94,9 +122,48 @@ async def get_context(
94122
- **repo_name**: Name of the repository to get context for.
95123
- **returns**: Complete context information including repository metadata.
96124
"""
125+
logger.info(repo_name)
97126
return await service.get_context_by_repo_name(repo_name)
98127

99128

129+
@context_router.get(
130+
"/{context_id}/stats",
131+
response_model=AnalyticQStatsModel,
132+
summary="Get stats by context id",
133+
responses={
134+
status.HTTP_404_NOT_FOUND: {"model": ErrorResponse, "description": " Stats not found"},
135+
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse, "description": "Internal server error"},
136+
}
137+
)
138+
async def get_stats_by_context_id(
139+
context_id: int,
140+
stats_service: AnalyticQStatsService = Depends(get_stats_service)
141+
):
142+
"""
143+
Retrieve statistics analysis by its context ID.
144+
145+
- **context_id**: ID of the context to get stats for.
146+
- **returns**: Statistics data associated with the context.
147+
"""
148+
return await stats_service.get_stats_by_context_id(context_id)
149+
150+
151+
@context_router.get(
152+
"/{id}",
153+
response_model=AnalyticQContextModel,
154+
summary="Get context by its id",
155+
responses={
156+
status.HTTP_404_NOT_FOUND: {"model": ErrorResponse, "description": "Context not found"},
157+
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse, "description": "Internal server error"},
158+
}
159+
)
160+
async def get_context_by_id(
161+
id: int,
162+
context_service: AnalyticQContextService = Depends(get_context_service)
163+
):
164+
return await context_service.get_context_by_id(id)
165+
166+
100167
@context_router.get(
101168
"/{repo_name}/scans",
102169
response_model=List[AnalyticQSASTScanResultModel],

analyticq-backend/analyticq/api/issues.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,61 @@
11
import logging
2+
from typing import Optional
23

34
from analyticq.repository.issue_repository import (
45
AnalyticQSASTIssueModel, AnalyticQSASTIssueRepository)
5-
from analyticq.schemas.issue_dto import IssueUpdateRequest
6+
from analyticq.schemas.error_dto import ErrorResponse
7+
from analyticq.schemas.issue_dto import (IssueUpdateRequest,
8+
PaginatedIssueResponse)
69
from analyticq.service import AnalyticQSASTIssueService
7-
from fastapi import APIRouter, Depends, status
8-
from pydantic import BaseModel
10+
from fastapi import APIRouter, Depends, Query, status
911

1012
logger = logging.getLogger(__name__)
1113

1214
issue_router = APIRouter(prefix="/issues", tags=["Issue Management"])
1315

1416

15-
class ErrorResponse(BaseModel):
16-
detail: str
17-
18-
1917
def get_issue_service(
2018
repo: AnalyticQSASTIssueRepository = Depends(AnalyticQSASTIssueRepository)
2119
) -> AnalyticQSASTIssueService:
2220
"""Dependency provider for issue service."""
2321
return AnalyticQSASTIssueService(repo)
2422

2523

24+
@issue_router.get(
25+
"/",
26+
summary="Get all issue paginated",
27+
response_model=PaginatedIssueResponse,
28+
responses={
29+
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse, "description": "Internal server error"},
30+
},
31+
)
32+
async def get_all_contexts(
33+
page: int = Query(1, ge=1, description="Page number"),
34+
page_size: Optional[int] = Query(10, ge=1, le=100, description="Number of items per page, set to 0 for all items"),
35+
service: AnalyticQSASTIssueService = Depends(get_issue_service)
36+
):
37+
if page_size == 0:
38+
# Get all contexts without pagination
39+
issues = await service.get_all_issues()
40+
return {
41+
"items": issues,
42+
"total": len(issues),
43+
"page": 1,
44+
"page_size": len(issues),
45+
"has_more": False
46+
}
47+
else:
48+
# Get contexts with pagination
49+
issues, total = await service.get_all_issues_paginated(page, page_size)
50+
return {
51+
"items": issues,
52+
"total": total,
53+
"page": page,
54+
"page_size": page_size,
55+
"has_more": (page * page_size) < total
56+
}
57+
58+
2659
@issue_router.get(
2760
"/{issue_id}",
2861
response_model=AnalyticQSASTIssueModel,

analyticq-backend/analyticq/api/scans.py

Lines changed: 36 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,76 @@
11
import logging
2-
from enum import Enum
32
from typing import List, Optional
43

54
from analyticq.repository.scan_repository import (
6-
AnalyticQSASTIssueModel, AnalyticQSASTScanResultModel,
7-
AnalyticQScanResultRepository)
8-
from analyticq.schemas.scan_dto import ScanCreateRequest
5+
AnalyticQSASTScanResultModel, AnalyticQScanResultRepository)
6+
from analyticq.schemas.error_dto import ErrorResponse
7+
from analyticq.schemas.scan_dto import PaginatedScanResponse
98
from analyticq.service import AnalyticQScanService
109
from fastapi import APIRouter, Depends, Query, status
11-
from pydantic import BaseModel
1210

1311
logger = logging.getLogger(__name__)
1412

1513
scan_router = APIRouter(prefix="/scans", tags=["Scan Management"])
1614

1715

18-
class ErrorResponse(BaseModel):
19-
detail: str
20-
21-
22-
class Severity(str, Enum):
23-
CRITICAL = "CRITICAL"
24-
HIGH = "HIGH"
25-
MEDIUM = "MEDIUM"
26-
LOW = "LOW"
27-
UNKNOWN = "UNKNOWN"
28-
29-
30-
class Confidence(str, Enum):
31-
CRITICAL = "CRITICAL"
32-
HIGH = "HIGH"
33-
MEDIUM = "MEDIUM"
34-
LOW = "LOW"
35-
UNKNOWN = "UNKNOWN"
36-
37-
3816
def get_scan_service(
3917
repo: AnalyticQScanResultRepository = Depends(AnalyticQScanResultRepository)
4018
) -> AnalyticQScanService:
4119
"""Dependency provider for scan service."""
4220
return AnalyticQScanService(repo)
4321

4422

45-
@scan_router.post(
23+
@scan_router.get(
4624
"/",
47-
response_model=AnalyticQSASTScanResultModel,
48-
status_code=status.HTTP_201_CREATED,
25+
summary="Get all scans paginated",
26+
response_model=PaginatedScanResponse,
4927
responses={
50-
status.HTTP_201_CREATED: {"description": "Scan created successfully"},
51-
status.HTTP_400_BAD_REQUEST: {"model": ErrorResponse},
52-
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse},
28+
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse, "description": "Internal server error"},
5329
},
5430
)
55-
async def create_scan(
56-
scan_data: ScanCreateRequest,
31+
async def get_all_scans_paginated(
32+
page: int = Query(1, ge=1, description="Page number"),
33+
page_size: Optional[int] = Query(10, ge=1, le=100, description="Number of items per page, set to 0 for all items"),
5734
service: AnalyticQScanService = Depends(get_scan_service)
5835
):
59-
"""
60-
Create a new scan record in the system.
61-
"""
62-
return await service.create_scan(scan_data)
36+
if page_size == 0:
37+
scans = await service.get_all_scan()
38+
return {
39+
"items": scans,
40+
"total": len(scans),
41+
"page": 1,
42+
"page_size": len(scans),
43+
"has_more": False
44+
}
45+
else:
46+
# Get scans with pagination
47+
scans, total = await service.get_all_scans_paginated(page, page_size)
48+
return {
49+
"items": scans,
50+
"total": total,
51+
"page": page,
52+
"page_size": page_size,
53+
"has_more": (page * page_size) < total
54+
}
6355

6456

6557
@scan_router.get(
66-
"/{scan_id}/issues",
67-
response_model=List[AnalyticQSASTIssueModel],
58+
"/{scan_id}",
59+
response_model=AnalyticQSASTScanResultModel,
60+
summary="Get a scans by ID",
6861
responses={
69-
status.HTTP_200_OK: {"description": "List of filtered issues"},
7062
status.HTTP_404_NOT_FOUND: {"model": ErrorResponse},
7163
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse},
7264
},
7365
)
74-
async def get_filtered_scan_issues(
75-
scan_id: str,
76-
severity: Optional[Severity] = Query(None, description="Filter issues by severity"),
77-
confidence: Optional[Confidence] = Query(None, description="Filter issues by confidence"),
66+
async def get_scan_by_id(
67+
scan_id: int,
7868
service: AnalyticQScanService = Depends(get_scan_service)
7969
):
8070
"""
8171
Retrieve a list of issues for a specific scan, optionally filtered by severity and confidence.
8272
"""
83-
return await service.filter_scan_issues(scan_id, severity, confidence)
73+
return await service.get_scan_by_id(scan_id)
8474

8575

8676
@scan_router.get(
@@ -98,25 +88,7 @@ async def get_scans_by_tool_name(
9888
"""
9989
Retrieve a list of scan results for a specific SAST tool.
10090
"""
101-
return await service.get_scans_by_tool_name(tool_name)
102-
103-
104-
@scan_router.get(
105-
"/tool/{tool_name}/issues",
106-
response_model=List[AnalyticQSASTIssueModel],
107-
responses={
108-
status.HTTP_200_OK: {"description": "List of issues for the specified tool"},
109-
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse},
110-
},
111-
)
112-
async def get_scan_issues_by_tool_name(
113-
tool_name: str,
114-
service: AnalyticQScanService = Depends(get_scan_service)
115-
):
116-
"""
117-
Retrieve all issues found by a specific security analysis tool.
118-
"""
119-
return await service.get_issues_by_tool_name(tool_name)
91+
return await service.get_scans_by_tool_name(tool_name.lower())
12092

12193

12294
@scan_router.delete(

0 commit comments

Comments
 (0)