Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.7.0] - 2026-02-18

### Added
- NDP Affinities integration for automatic registration of datasets and services
- New configuration: `AFFINITIES_ENABLED`, `AFFINITIES_URL`, `AFFINITIES_EP_UUID`, `AFFINITIES_TIMEOUT`
- AffinitiesClient module for async HTTP communication with Affinities API
- Automatic dataset registration in Affinities on `POST /dataset`
- Automatic service registration in Affinities on `POST /services`
- Automatic endpoint relationships created for datasets and services
- Non-blocking integration: Affinities errors don't affect main operations
- Documentation: `docs/affinities-integration.md`

## [0.6.1] - 2026-02-12

### Changed
- Version bump for Docker image release

## [0.6.0] - 2026-02-02

### Added
Expand Down
51 changes: 51 additions & 0 deletions api/config/affinities_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# api/config/affinities_settings.py
"""
Affinities API integration configuration.

This module provides configuration settings for connecting to
the NDP Affinities service for automatic registration of
datasets, services, and their relationships.
"""

from pydantic_settings import BaseSettings


class AffinitiesSettings(BaseSettings):
"""
Configuration settings for Affinities integration.

Attributes
----------
enabled : bool
Enable/disable Affinities integration (default: False)
url : str
Base URL of the Affinities API (e.g., "http://affinities-api:8000")
ep_uuid : str
UUID of this endpoint in Affinities, obtained when manually
registering the endpoint in the Affinities system
timeout : int
Request timeout in seconds (default: 30)
"""

enabled: bool = False
url: str = ""
ep_uuid: str = ""
timeout: int = 30

model_config = {
"env_file": ".env",
"extra": "allow",
"env_prefix": "AFFINITIES_",
}

@property
def is_configured(self) -> bool:
"""
Check if Affinities integration is properly configured.

Returns True only if enabled AND both url and ep_uuid are set.
"""
return self.enabled and bool(self.url) and bool(self.ep_uuid)


affinities_settings = AffinitiesSettings()
22 changes: 22 additions & 0 deletions api/routes/register_routes/post_general_dataset.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# api/routes/register_routes/post_general_dataset.py

import logging
from typing import Any, Dict, Literal

from fastapi import APIRouter, Depends, HTTPException, Query, status

from api.config import catalog_settings, ckan_settings
from api.models.general_dataset_request_model import GeneralDatasetRequest
from api.repositories import CKANRepository
from api.services.affinities_services import AffinitiesClient
from api.services.auth_services import get_user_for_write_operation
from api.services.dataset_services.general_dataset import create_general_dataset

logger = logging.getLogger(__name__)

router = APIRouter()


Expand Down Expand Up @@ -195,6 +199,24 @@ async def create_general_dataset_endpoint(
repository=repository,
user_info=user_info,
)

# Register in Affinities (non-blocking, errors are logged)
affinities_client = AffinitiesClient()
if affinities_client.is_enabled:
try:
await affinities_client.register_dataset(
title=data.title,
metadata={
"name": data.name,
"owner_org": data.owner_org,
"local_id": dataset_id,
"notes": data.notes,
"tags": data.tags,
},
)
except Exception as e:
logger.warning(f"Failed to register dataset in Affinities: {e}")

return {"id": dataset_id}

except ValueError as exc:
Expand Down
23 changes: 23 additions & 0 deletions api/routes/register_routes/post_service.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# api/routes/register_routes/post_service.py
import logging
from typing import Any, Dict, Literal

from fastapi import APIRouter, Depends, HTTPException, Query, status

from api.config import catalog_settings, ckan_settings
from api.models.service_request_model import ServiceRequest
from api.repositories import CKANRepository
from api.services.affinities_services import AffinitiesClient
from api.services.auth_services import get_user_for_write_operation
from api.services.service_services.add_service import add_service

logger = logging.getLogger(__name__)

router = APIRouter()


Expand Down Expand Up @@ -192,6 +196,25 @@ async def create_service(
ckan_instance=ckan_instance,
user_info=user_info,
)

# Register in Affinities (non-blocking, errors are logged)
affinities_client = AffinitiesClient()
if affinities_client.is_enabled:
try:
await affinities_client.register_service(
service_type=data.service_type,
openapi_url=data.documentation_url,
metadata={
"service_name": data.service_name,
"service_title": data.service_title,
"service_url": data.service_url,
"local_id": service_id,
"notes": data.notes,
},
)
except Exception as e:
logger.warning(f"Failed to register service in Affinities: {e}")

return {"id": service_id}

except ValueError as exc:
Expand Down
11 changes: 11 additions & 0 deletions api/services/affinities_services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# api/services/affinities_services/__init__.py
"""
Affinities integration services.

This package provides services for integrating with the NDP Affinities API
to automatically register datasets, services, and their relationships.
"""

from api.services.affinities_services.affinities_client import AffinitiesClient

__all__ = ["AffinitiesClient"]
Loading
Loading