diff --git a/.env.example b/.env.example index 54ea343..b69ece3 100644 --- a/.env.example +++ b/.env.example @@ -15,3 +15,16 @@ MONGODB_USERNAME="mongo_user" MONGODB_PASSWORD="mongo_password" MONGODB_DATABASE="sientia" +# OPC Gateway +NATS_URL="nats://localhost:4222" +NATS_WRITE_SUBJECT="opc.write" +NATS_QUEUE="gateway-writers" +GRPC_ENABLED="false" +GRPC_PORT="50051" +CERT_BUCKET="sientia-dataops" +CERT_CACHE_DIR="/tmp/opc-certs" +WRITE_TIMESTAMP="true" +WRITE_QUALITY="true" +WRITE_MIN_PAUSE_S="0.0" +IDLE_CONN_MAX_S="300.0" + diff --git a/ingestor/config.py b/ingestor/config.py new file mode 100644 index 0000000..3d0bf20 --- /dev/null +++ b/ingestor/config.py @@ -0,0 +1,46 @@ +"""Gateway-specific env config (NATS/gRPC/certs/write flags). + +Separate from ingestor.py's inline os.getenv (Redis/Mongo/TTLs) — this module +covers only the new gateway wiring consumed by app.py in Task 18. +""" + +from dataclasses import dataclass +from os import getenv + + +def _env_bool(name: str, default: bool) -> bool: + value = getenv(name) + if value is None: + return default + return value.strip().lower() in ('true', '1', 'yes') + + +@dataclass +class GatewayConfig: + nats_url: str = 'nats://localhost:4222' + nats_write_subject: str = 'opc.write' + nats_queue: str = 'gateway-writers' + grpc_enabled: bool = False + grpc_port: int = 50051 + cert_bucket: str = 'sientia-dataops' + cert_cache_dir: str = '/tmp/opc-certs' # noqa: S108 - default is spec'd, overridable via env + write_timestamp: bool = True + write_quality: bool = True + write_min_pause_s: float = 0.0 + idle_conn_max_s: float = 300.0 + + @classmethod + def from_env(cls) -> 'GatewayConfig': + return cls( + nats_url=getenv('NATS_URL', cls.nats_url), + nats_write_subject=getenv('NATS_WRITE_SUBJECT', cls.nats_write_subject), + nats_queue=getenv('NATS_QUEUE', cls.nats_queue), + grpc_enabled=_env_bool('GRPC_ENABLED', cls.grpc_enabled), + grpc_port=int(getenv('GRPC_PORT', cls.grpc_port)), + cert_bucket=getenv('CERT_BUCKET', cls.cert_bucket), + cert_cache_dir=getenv('CERT_CACHE_DIR', cls.cert_cache_dir), + write_timestamp=_env_bool('WRITE_TIMESTAMP', cls.write_timestamp), + write_quality=_env_bool('WRITE_QUALITY', cls.write_quality), + write_min_pause_s=float(getenv('WRITE_MIN_PAUSE_S', cls.write_min_pause_s)), + idle_conn_max_s=float(getenv('IDLE_CONN_MAX_S', cls.idle_conn_max_s)), + ) diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py new file mode 100644 index 0000000..839e588 --- /dev/null +++ b/tests/unit/test_config.py @@ -0,0 +1,60 @@ +import pytest + +from ingestor.config import GatewayConfig + +_ENV_VARS = ( + 'NATS_URL', 'NATS_WRITE_SUBJECT', 'NATS_QUEUE', 'GRPC_ENABLED', 'GRPC_PORT', + 'CERT_BUCKET', 'CERT_CACHE_DIR', 'WRITE_TIMESTAMP', 'WRITE_QUALITY', + 'WRITE_MIN_PAUSE_S', 'IDLE_CONN_MAX_S', +) + + +@pytest.fixture(autouse=True) +def clean_env(monkeypatch): + for var in _ENV_VARS: + monkeypatch.delenv(var, raising=False) + + +def test_from_env_defaults(): + cfg = GatewayConfig.from_env() + + assert cfg.nats_url == 'nats://localhost:4222' + assert cfg.nats_write_subject == 'opc.write' + assert cfg.nats_queue == 'gateway-writers' + assert cfg.grpc_enabled is False + assert cfg.grpc_port == 50051 + assert cfg.cert_bucket == 'sientia-dataops' + assert cfg.cert_cache_dir == '/tmp/opc-certs' # noqa: S108 + assert cfg.write_timestamp is True + assert cfg.write_quality is True + assert cfg.write_min_pause_s == 0.0 + assert cfg.idle_conn_max_s == 300.0 + + +def test_from_env_overrides(monkeypatch): + monkeypatch.setenv('GRPC_ENABLED', 'true') + monkeypatch.setenv('GRPC_PORT', '6000') + monkeypatch.setenv('WRITE_MIN_PAUSE_S', '0.5') + monkeypatch.setenv('WRITE_QUALITY', 'false') + + cfg = GatewayConfig.from_env() + + assert cfg.grpc_enabled is True + assert cfg.grpc_port == 6000 + assert cfg.write_min_pause_s == 0.5 + assert cfg.write_quality is False + + +@pytest.mark.parametrize('value,expected', [ + ('0', False), + ('no', False), + ('FALSE', False), + ('1', True), + ('YES', True), +]) +def test_bool_parsing_edge_cases(monkeypatch, value, expected): + monkeypatch.setenv('GRPC_ENABLED', value) + + cfg = GatewayConfig.from_env() + + assert cfg.grpc_enabled is expected