Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7499852
refactor(services): expose public symbols via __init__.py and update …
rusiru-erandaka Jun 24, 2026
a28e2c2
refactor(core): expose public symbols via __init__.py
rusiru-erandaka Jun 24, 2026
0d9884b
refactor(exception): expose public symbols via __init__.py
rusiru-erandaka Jun 24, 2026
87f7643
refactor(middleware): expose public symbols via __init__.py and updat…
rusiru-erandaka Jun 24, 2026
776e8e3
refactor(models): expose public symbols via __init__.py
rusiru-erandaka Jun 24, 2026
08357db
refactor(utils): expose public symbols via __init__.py and update htt…
rusiru-erandaka Jun 24, 2026
ae0fcd0
refactor(routers): expose public symbols via __init__.py and update r…
rusiru-erandaka Jun 24, 2026
c470e1b
refactor(main): update imports to use package-level interface
rusiru-erandaka Jun 24, 2026
faaeeb5
test: update test imports to use package-level interface
rusiru-erandaka Jun 24, 2026
23c766f
chore: update .env.example with new service configuration
rusiru-erandaka Jun 24, 2026
591bf41
Update package import alphabetically src/services/__init__.py
rusiru-erandaka Jun 24, 2026
c734f1d
Update according to the alphabetical oder. src/services/__init__.py
rusiru-erandaka Jun 24, 2026
ece2c1e
Update importers according to alphabetical oder. src/models/__init__.py
rusiru-erandaka Jun 24, 2026
c869033
Return department ids for links of cabinet flow response
zaeema-n Jun 26, 2026
db52c27
Add endpoint to resolve entity IDs to display names
zaeema-n Jun 26, 2026
f8ad791
Refactor organisation_service to simplify department link aggregation…
zaeema-n Jun 26, 2026
9a38e5f
added relative imports instead of absolute imports in enums
rusiru-erandaka Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
BASE_URL_QUERY=base-url-query
BASE_URL_QUERY=BASE_URL_QUERY
# Comma-separated list of allowed origins for CORS (replace as needed)
ALLOWED_ORIGINS=http://localhost:3000

Expand Down
8 changes: 4 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
person_router,
document_router,
)
from src.core.config import settings
from src.core import settings
from fastapi.middleware.cors import CORSMiddleware
from src.middleware.throttling import ThrottlingMiddleware
from src.utils.http_client import http_client
from src.middleware import ThrottlingMiddleware
from src.utils import http_client
from contextlib import asynccontextmanager

@asynccontextmanager
Expand Down Expand Up @@ -46,4 +46,4 @@ async def lifespan(app: FastAPI):
app.include_router(data_router)
app.include_router(search_router)
app.include_router(person_router)
app.include_router(document_router)
app.include_router(document_router)
4 changes: 4 additions & 0 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Top-level package for the GI Service application."""

# Intentionally avoid re-exporting subpackages here to keep imports explicit.
__all__: list[str] = []
6 changes: 6 additions & 0 deletions src/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .config import Settings, settings

__all__ = [
"Settings",
"settings",
]
15 changes: 15 additions & 0 deletions src/exception/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .exceptions import (
BadRequestError,
GatewayTimeoutError,
InternalServerError,
NotFoundError,
ServiceUnavailableError,
)

__all__ = [
"BadRequestError",
"GatewayTimeoutError",
"InternalServerError",
"NotFoundError",
"ServiceUnavailableError",
]
5 changes: 5 additions & 0 deletions src/middleware/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .throttling import ThrottlingMiddleware

__all__ = [
"ThrottlingMiddleware",
]
2 changes: 1 addition & 1 deletion src/middleware/throttling.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from src.core.config import settings
from src.core import settings

logger = logging.getLogger(__name__)

Expand Down
28 changes: 27 additions & 1 deletion src/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
from .data_requestbody import DataCatalogRequest, DatasetYearsRequest
from .organisation_schemas import (
AttributeFilterRecord,
AttributeFilterRecords,
Category,
Dataset,
Date,
Entity,
Kind,
Label,
Relation,
)
from .person_schemas import PersonSource, PersonResponse
Comment thread
rusiru-erandaka marked this conversation as resolved.
Outdated
from .search_schemas import SearchResponse, SearchResult

__all__ = [
"AttributeFilterRecord",
"AttributeFilterRecords",
"Category",
"DataCatalogRequest",
"Dataset",
"DatasetYearsRequest",
"Date",
"Entity",
"Kind",
"Label",
"PersonSource",
"PersonResponse"
"PersonResponse",
Comment thread
rusiru-erandaka marked this conversation as resolved.
"Relation",
"SearchResponse",
"SearchResult",
]
6 changes: 2 additions & 4 deletions src/routers/data_router.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from src.models.data_requestbody import DatasetYearsRequest
from src.models.data_requestbody import DataCatalogRequest
from fastapi.param_functions import Depends, Query, Body, Path
from src.services.data_service import DataService
from src.services.opengin_service import OpenGINService
from fastapi import APIRouter
from pydantic import BaseModel, Field
from src.models import DataCatalogRequest, DatasetYearsRequest
from src.services import DataService, OpenGINService

router = APIRouter(prefix="/v1/data", tags=["Data"])

Expand Down
2 changes: 1 addition & 1 deletion src/routers/organisation_router.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fastapi import APIRouter, Depends, Query, Body, Path
from src.models.organisation_schemas import Date
from src.models import Date
from src.services import OpenGINService, OrganisationService
from typing import Sequence

Expand Down
5 changes: 2 additions & 3 deletions src/routers/search_router.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from fastapi import APIRouter, Depends, Query
from typing import Optional, List
from datetime import datetime
from src.services.search_service import SearchService
from src.services.opengin_service import OpenGINService
from src.models.search_schemas import SearchResponse
from src.models import SearchResponse
from src.services import OpenGINService, SearchService

router = APIRouter(prefix="/v1/search", tags=["Search"])

Expand Down
8 changes: 6 additions & 2 deletions src/services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from .data_service import DataService
from .organisation_service import OrganisationService
from .opengin_service import OpenGINService
from .person_service import PersonService
from .document_service import DocumentService
from .search_service import SearchService
Comment thread
rusiru-erandaka marked this conversation as resolved.

__all__ = [
"DataService",
"DocumentService",
"OrganisationService",
"OpenGINService",
Comment thread
rusiru-erandaka marked this conversation as resolved.
Outdated
"PersonService",
"DocumentService"
]
"SearchService",
]
10 changes: 3 additions & 7 deletions src/services/data_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@
import asyncio
from typing import Dict
from src.enums import KindMajorEnum, KindMinorEnum, RelationNameEnum, RelationDirectionEnum
from src.exception.exceptions import InternalServerError, NotFoundError
from src.exception.exceptions import BadRequestError
from src.models.organisation_schemas import Relation
from src.utils.util_functions import Util
from src.models.organisation_schemas import Kind
from src.models.organisation_schemas import Entity
from src.exception import BadRequestError, InternalServerError, NotFoundError
from src.models import Entity, Kind, Relation
from aiohttp import ClientSession
from src.utils import http_client
from src.utils import Util, http_client

logger = logging.getLogger(__name__)

Expand Down
11 changes: 5 additions & 6 deletions src/services/document_service.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from src.exception.exceptions import InternalServerError
import asyncio
from src.utils.util_functions import Util
from aiohttp import ClientSession
from src.utils import http_client
from src.models.organisation_schemas import Entity, Kind
from src.enums.kindEnum import KindMajorEnum, KindMinorEnum
from typing import Dict, List
from datetime import datetime
import logging
from typing import Dict, List
from src.enums import KindMajorEnum, KindMinorEnum
from src.exception import InternalServerError
from src.models import Entity, Kind
from src.utils import Util, http_client

logger = logging.getLogger(__name__)

Expand Down
17 changes: 5 additions & 12 deletions src/services/opengin_service.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
from asyncio import timeout
from google.api_core.exceptions import GoogleAPICallError
from google.api_core.retry import if_transient_error
from src.models.organisation_schemas import AttributeFilterRecords
from src.models.organisation_schemas import Entity, Relation
from src.exception.exceptions import BadRequestError
from src.exception.exceptions import InternalServerError
from src.exception.exceptions import NotFoundError
from google.api_core import retry_async
from google.api_core import exceptions
from aiohttp import ClientSession
from src.utils.http_client import http_client
from src.core.config import settings
import logging
from src.core import settings
from src.exception import BadRequestError, InternalServerError, NotFoundError
from src.models import AttributeFilterRecords, Entity, Relation
from src.utils import http_client

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -194,4 +187,4 @@ async def get_attributes(
raise InternalServerError("An unexpected error occurred") from e




16 changes: 5 additions & 11 deletions src/services/organisation_service.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
from src.enums.relationEnum import RelationDirectionEnum
from src.enums.relationEnum import RelationNameEnum
from src.exception.exceptions import BadRequestError
from src.exception.exceptions import NotFoundError
from src.exception.exceptions import InternalServerError
import asyncio
from src.utils.util_functions import Util
from aiohttp import ClientSession
from src.utils import http_client
from src.models.organisation_schemas import Entity, Relation
from src.enums.idEnum import EntityIdEnum
from typing import Optional, Sequence
import logging
from typing import Optional, Sequence
from src.enums import EntityIdEnum, RelationDirectionEnum, RelationNameEnum
from src.exception import BadRequestError, InternalServerError, NotFoundError
from src.models import Entity, Relation
from src.utils import Util, http_client

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -800,4 +795,3 @@ async def department_history_timeline(self, department_id: str):
except Exception as e:
logger.error(f"Error in enrich_department_timeline: {e}")
raise InternalServerError("An unexpected error occurred") from e

15 changes: 4 additions & 11 deletions src/services/person_service.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
from src.enums.relationEnum import RelationDirectionEnum
from src.enums.relationEnum import RelationNameEnum
from src.exception.exceptions import BadRequestError
from src.exception.exceptions import NotFoundError
from src.exception.exceptions import InternalServerError
import asyncio
from src.utils.util_functions import Util
from aiohttp import ClientSession
from src.utils import http_client
from src.models.organisation_schemas import Entity, Relation, Kind
from src.enums.kindEnum import KindMajorEnum, KindMinorEnum
from src.enums.idEnum import EntityIdEnum
from src.models.person_schemas import PersonResponse
from datetime import datetime

import logging
from src.enums import EntityIdEnum, KindMajorEnum, KindMinorEnum, RelationDirectionEnum, RelationNameEnum
from src.exception import BadRequestError, InternalServerError, NotFoundError
from src.models import Entity, Kind, PersonResponse, Relation
from src.utils import Util, http_client

logger = logging.getLogger(__name__)

Expand Down
8 changes: 3 additions & 5 deletions src/services/search_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import logging
from typing import List, Dict, Any, Optional
from src.enums import KindMajorEnum, KindMinorEnum
from src.exception.exceptions import BadRequestError, InternalServerError
from src.models.organisation_schemas import Entity, Kind
from src.models.search_schemas import SearchResult, SearchResponse
from src.utils.util_functions import Util
from src.exception import BadRequestError, InternalServerError
from src.models import Entity, Kind, SearchResponse, SearchResult
from src.utils import Util

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -222,4 +221,3 @@ def _determine_entity_type(self, major: str, minor: str) -> str:

return entity_type


8 changes: 8 additions & 0 deletions src/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .http_client import HTTPClient, http_client
from .util_functions import Util

__all__ = [
"HTTPClient",
"http_client",
"Util",
]
4 changes: 2 additions & 2 deletions src/utils/http_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from aiohttp import ClientSession, ClientTimeout, TCPConnector
from typing import Optional
from src.core.config import settings
from src.core import settings
class HTTPClient:
"Single HTTP client for the application"

Expand Down Expand Up @@ -48,4 +48,4 @@ def session(self) -> ClientSession:
return self._session

# Create a global instance
http_client = HTTPClient()
http_client = HTTPClient()
6 changes: 6 additions & 0 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Test package for GI Service.

Fixtures and test helpers stay module-local to avoid creating a public API.
"""

__all__: list[str] = []
10 changes: 2 additions & 8 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@

from src.services.data_service import DataService
import pytest
from aiohttp import ClientError
from unittest.mock import patch, PropertyMock, MagicMock
from services.opengin_service import OpenGINService
from src.utils.http_client import HTTPClient
from services.organisation_service import OrganisationService
from unittest.mock import AsyncMock
from src.utils.util_functions import Util
from src.services.person_service import PersonService
from src.services.document_service import DocumentService
from src.services import DataService, DocumentService, OpenGINService, OrganisationService, PersonService
from src.utils import HTTPClient, Util

# MockResponse class to simulate aiohttp responses
class MockResponse:
Expand Down
2 changes: 1 addition & 1 deletion test/test_attribute_filter_schemas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from pydantic import ValidationError

from src.models.organisation_schemas import AttributeFilterRecord, AttributeFilterRecords
from src.models import AttributeFilterRecord, AttributeFilterRecords


def test_attribute_filter_record_defaults_operator_to_eq():
Expand Down
4 changes: 2 additions & 2 deletions test/test_data_service.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest
import asyncio
from src.exception.exceptions import InternalServerError, BadRequestError, NotFoundError
from unittest.mock import patch
from src.models.organisation_schemas import Entity, Relation, Kind
from src.enums import KindMajorEnum, KindMinorEnum, RelationNameEnum, RelationDirectionEnum
from src.exception import BadRequestError, InternalServerError, NotFoundError
from src.models import Entity, Kind, Relation

# Tests for enrich_dataset
@pytest.mark.asyncio
Expand Down
5 changes: 1 addition & 4 deletions test/test_document_service.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from src.services.document_service import DocumentService
from src.exception.exceptions import InternalServerError, NotFoundError
from src.models.organisation_schemas import Entity
from src.exception import InternalServerError, NotFoundError

@pytest.mark.asyncio
async def test_get_gazette_data_points_success(document_service, mock_opengin_service):
Expand Down Expand Up @@ -142,4 +140,3 @@ async def test_get_gazette_data_points_robustness(document_service, mock_opengin
assert result["data"][1]["year"] == 2024
assert result["data"][1]["values"][4] == 1 # May


10 changes: 4 additions & 6 deletions test/test_opengin_service.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@

import pytest
from src.enums.relationEnum import RelationDirectionEnum
from src.enums.relationEnum import RelationNameEnum
from src.models.organisation_schemas import Kind
from src.exception.exceptions import NotFoundError, BadRequestError
from src.models.organisation_schemas import (
from src.enums import RelationDirectionEnum, RelationNameEnum
from src.exception import BadRequestError, NotFoundError
from src.models import (
AttributeFilterRecord,
AttributeFilterRecords,
Entity,
Relation,
Kind,
)
from test.conftest import MockResponse

Expand Down Expand Up @@ -374,4 +373,3 @@ async def test_get_attributes_bad_request(mock_service, mock_session):
with pytest.raises(BadRequestError, match="Bad request"):
await mock_service.get_attributes("category_123", "dataset_abc")


Loading